Esempio n. 1
0
        public void Parse(bool simulator=true)
        {
            var rootNode = _doc.SelectNodes("ussd");
           
            _screens = new List<UssdScreen>();
            _scripts = new List<UssdScript>();
            _actions = new List<UssdAction>();
            _formLists = new List<UssdFormList>();
            _forms = new List<UssdForm>();
            _menus = new List<UssdMenu>();
            _boundModels = new  Dictionary<string, dynamic>();


            if (rootNode==null)
            {
                throw new Exception("no \"ussd\" element found");
            }

            //gather scripts
            foreach (XmlNode node in rootNode)
            {
                foreach (XmlNode ussdElement in node.ChildNodes)
                {
                    if (ussdElement.Name == "script")
                    {
                        if (ussdElement.Attributes.GetNamedItem("id") == null)
                        {
                            throw new Exception("script attribute \"id\" is required");
                        }

                       
                        var scriptId = ussdElement.Attributes["id"].InnerText;

                        Console.WriteLine("gotten script {0}", scriptId);
                        if (_screens.Any(s => s.Id == scriptId))
                        {
                            throw new Exception($"script id already exists at {scriptId}");
                        }
                        
                        var script = new UssdScript
                        {
                            Id = scriptId,
                            Content = ussdElement.InnerText,
                            BindFrom = ussdElement.Attributes.GetNamedItem("bindfrom") != null?ussdElement.Attributes["bindfrom"].InnerText:"",
                            BindTo= ussdElement.Attributes.GetNamedItem("bindto") != null?ussdElement.Attributes["bindto"].InnerText:"",
                            
                        };

                        _scripts.Add(script);
                        Console.WriteLine("added script {0}",scriptId);
                    }
                }
            }


            foreach (XmlNode node in rootNode)
            {
                foreach (XmlNode ussdElement in node.ChildNodes)
                {

                    #region screens

                    if (ussdElement.Name=="screen")
                    {
                        if (ussdElement.Attributes["id"] == null)
                        {
                            throw new Exception("screen id is required");
                        }
                        var screenId = ussdElement.Attributes["id"].InnerText;

                        Console.WriteLine("gotten screen {0}", screenId);
                        if (_screens.Any(s => s.Id == screenId))
                        {
                            throw new Exception($"screen id already exists at {screenId}");
                        }

                        var ussdScreen = new UssdScreen
                        {
                            Id = screenId,

                        };

                        foreach (XmlNode screenItem in ussdElement.ChildNodes)
                        {
                            #region Menu Element
                            if (screenItem.Name.Equals("menu"))
                            {
                                var menuNodes = screenItem.ChildNodes;

                                var ussdMenu = new UssdMenu();
                                if (screenItem.Attributes.Count > 0)
                                {
                                    if (screenItem.Attributes["bindfrom"] != null)
                                    {
                                        //todo: always check if there's a corresponding bindto in another screen
                                        ussdMenu.BindFrom = new UssdBoundModel
                                        {
                                            Name = screenItem.Attributes["bindfrom"].InnerText
                                        };

                                    }
                                    if (screenItem.Attributes["bindaction"] != null)
                                    {

                                        ussdMenu.BindAction = new UssdBoundAction(screenItem.Attributes["bindaction"].InnerText);

                                    }

                                }

                                foreach (XmlNode menuNode in menuNodes)
                                {

                                    if (menuNode.Name == "title")
                                    {
                                        ussdMenu.Title = menuNode.InnerText.ToUssdString(simulator);
                                    }

                                    if (menuNode.Name == "option")
                                    {
                                        var menuOption = new UssdMenuOption
                                        {
                                            Text = menuNode.InnerText,
                                            Value = menuNode.Attributes["value"].InnerText.ToUssdString(simulator),
                                            OnSelect = new UssdNavigator(menuNode.Attributes["onselect"].InnerText)
                                        };

                                        ussdMenu.Options.Add(menuOption);


                                    }
                                }


                                ussdScreen.UssdItems.Add(ussdMenu);
                                _menus.Add(ussdMenu);
                                Console.WriteLine("added menu {0}", ussdMenu.Title);
                            }
                            #endregion

                            #region Form Element
                            if (screenItem.Name.Equals("form"))
                            {
                                var formNodes = screenItem.ChildNodes;

                                var ussdForm = new UssdForm();
                                if (screenItem.Attributes.Count > 0)
                                {
                                    if (screenItem.Attributes["bindto"] != null)
                                    {
                                        ussdForm.BindTo = new UssdBoundModel
                                        {
                                            Name = screenItem.Attributes["bindto"].InnerText
                                        };
                                        //_boundModels[screenItem.Attributes["bindto"].InnerText] = new Dictionary<string, string>();

                                    }
                                    if (screenItem.Attributes["onsubmit"] != null)
                                    {
                                        ussdForm.OnSubmit = new UssdNavigator(screenItem.Attributes["onsubmit"].InnerText);

                                    }
                                    else
                                    {
                                        throw new Exception("form attribute \"onsubmit\" is required");
                                    }


                                }

                                foreach (XmlNode formNode in formNodes)
                                {

                                    if (formNode.Name == "title")
                                    {
                                        ussdForm.Title = formNode.InnerText.ToUssdString(simulator);
                                    }

                                    if (formNode.Name == "input")
                                    {
                                        var ussdFormInput = new UssdFormInput
                                        {

                                            Id = formNode.Attributes["id"].InnerText,
                                            Type = formNode.Attributes["type"].InnerText,
                                            Display = formNode.Attributes["display"].Value.ToUssdString(simulator),
                                            Required =
                                                                 bool.Parse(formNode.Attributes["required"].InnerText),
                                            MaxLength = formNode.Attributes["maxlength"] == null ? (int?)null : int.Parse(formNode.Attributes["maxlength"].InnerText)
                                        };

                                        if (ussdFormInput.Type == "list")
                                        {
                                            ussdFormInput.IsList = true;
                                            ussdFormInput.ListId = formNode.Attributes["listid"].InnerText;
                                        }



                                        ussdForm.Inputs.Add(ussdFormInput);


                                    }
                                    if (formNode.Name == "list")
                                    {


                                        var formListNodes = formNode.ChildNodes;

                                        var ussdFormList = new UssdFormList { Id = formNode.Attributes["id"].InnerText };



                                        bool hasRepeater = formNode.Attributes.GetNamedItem("repeater") != null;

                                        if (hasRepeater)
                                        {
                                       
                                         
                                            ussdFormList.Repeater = formNode.Attributes["repeater"].InnerText;

                                            var scriptItem =
                                                _scripts.FirstOrDefault(s => s.Id == ussdFormList.RepeaterScriptId);
                                            if (scriptItem == null)
                                            {
                                                throw new Exception(
                                                    $"script id \"{ussdFormList.RepeaterScriptId}\" not found");
                                            }

                                            
                                        }
                                        else
                                        {
                                            foreach (XmlNode formListNode in formListNodes)
                                            {

                                                ussdFormList.Choices.Add(new UssdFormListChoice
                                                {
                                                    Text = formListNode.InnerText,
                                                    Selector = formListNode.Attributes["selector"].InnerText,
                                                    Value = formListNode.Attributes["value"].InnerText
                                                });
                                            }
                                        }

                                        _formLists.Add(ussdFormList);

                                    }
                                }


                                ussdScreen.UssdItems.Add(ussdForm);
                                _forms.Add(ussdForm);

                                Console.WriteLine("added form {0}", ussdForm.Title);
                            }

                            #endregion

                            #region Text Element
                            if (screenItem.Name.Equals("text"))
                            {
                                var ussdText = new UssdText(screenItem.InnerText.ToUssdString(simulator));
                                if (screenItem.Attributes.Count > 0)
                                {
                                    if (screenItem.Attributes["bindfrom"] != null)
                                    {
                                        //todo: always check if there's a corresponding bindto in another screen
                                        ussdText.BindFrom = new UssdBoundModel
                                        {
                                            Name = screenItem.Attributes["bindfrom"].InnerText
                                        };

                                    }
                                    if (screenItem.Attributes["bindaction"] != null)
                                    {

                                        ussdText.BindAction = new UssdBoundAction(screenItem.Attributes["bindaction"].InnerText);

                                    }

                                }
                                ussdScreen.UssdItems.Add(ussdText);
                            }
                            #endregion

                            #region Action Element
                            if (screenItem.Name.Equals("action"))
                            {
                                var actionNode = screenItem.ChildNodes;

                                var ussdAction = new UssdAction();
                                if (screenItem.Attributes.Count > 0)
                                {
                                    if (screenItem.Attributes["id"] != null)
                                    {
                                        ussdAction.Id = screenItem.Attributes["id"].InnerText;

                                    }
                                    else
                                    {
                                        throw new Exception("action attribute id is required");
                                    }

                                    if (screenItem.Attributes["bindto"] != null)
                                    {
                                        ussdAction.BindTo = new UssdBoundModel
                                        {
                                            Name = screenItem.Attributes["bindto"].InnerText
                                        };
                                     //   _boundModels[screenItem.Attributes["bindto"].InnerText] = new Dictionary<string, string>();

                                    }
                                    else
                                    {
                                        throw new Exception("action attribute bindto is required");
                                    }

                                    if (screenItem.Attributes["cycle"] != null)
                                    {
                                        UssdActionCycle cycle;
                                        if (!Enum.TryParse(screenItem.Attributes["cycle"].InnerText, true, out cycle))
                                        {
                                            throw new Exception(String.Format("action attribute cycle has an unknown value. Allowed values are: {0} or {1}", UssdActionCycle.Always, UssdActionCycle.Once));
                                        }

                                        ussdAction.Http.Cycle = cycle;
                                    }
                                }
                                else
                                {
                                    throw new Exception("form attribute id and bindto is required");
                                }

                                foreach (XmlNode xmlNode in actionNode)
                                {


                                    if (xmlNode.Attributes.Count > 0)
                                    {
                                        if (xmlNode.Attributes["url"] != null)
                                        {
                                            ussdAction.Http.Url = xmlNode.Attributes["url"].InnerText.ToUssdString(simulator);

                                        }
                                        else
                                        {
                                            throw new Exception("http attribute url is required");
                                        }
                                        if (xmlNode.Attributes["method"] != null)
                                        {
                                            UssdActionHttpMethod httpMethod;
                                            if (!Enum.TryParse(xmlNode.Attributes["method"].InnerText, true, out httpMethod))
                                            {
                                                throw new Exception("http attribute method is invalid");
                                            }

                                            ussdAction.Http.Method = httpMethod;
                                        }
                                        else
                                        {
                                            throw new Exception("http attribute method is required");
                                        }
                                    }


                                    if (xmlNode.Name == "http")
                                    {
                                        var httpNodes = xmlNode.ChildNodes;

                                        foreach (XmlNode httpNode in httpNodes)
                                        {
                                            if (httpNode.Name == "header")
                                            {
                                                var headers = httpNode.ChildNodes;

                                                foreach (XmlNode header in headers)
                                                {
                                                    if (header.Name == "add")
                                                    {
                                                        var ussdActionHttpHeader = new UssdActionHttpHeader
                                                        {

                                                            Name = header.Attributes["key"].InnerText,
                                                            Value = header.Attributes["value"].InnerText,

                                                        };

                                                        ussdAction.Http.Headers.Add(ussdActionHttpHeader);
                                                    }

                                                }
                                            }
                                            if (httpNode.Name == "body")
                                            {
                                                if (httpNode.Attributes["bindfrom"] != null)
                                                {

                                                    ussdAction.Http.Body.BindFrom = new UssdBoundModel
                                                    {
                                                        Name = httpNode.Attributes["bindfrom"].InnerText
                                                    };

                                                }

                                                var headers = httpNode.ChildNodes;

                                                foreach (XmlNode header in headers)
                                                {
                                                    if (header.Name == "add")
                                                    {
                                                        var ussdActionHttpBody = new UssdActionHttpBodyParam
                                                        {

                                                            Name = header.Attributes["key"].InnerText,
                                                            Value = header.Attributes["value"].InnerText,

                                                        };

                                                        ussdAction.Http.Body.Params.Add(ussdActionHttpBody);
                                                    }

                                                }
                                            }
                                            if (httpNode.Name == "response")
                                            {
                                                var responses = httpNode.ChildNodes;

                                                foreach (XmlNode response in responses)
                                                {
                                                    if (response.Name == "code")
                                                    {
                                                        var ussdActionHttpResponse = new UssdActionHttpResponse
                                                        {

                                                            Value = int.Parse(response.Attributes["value"].InnerText),
                                                            Goto = new UssdNavigator(response.Attributes["goto"].InnerText),

                                                        };

                                                        ussdAction.Http.Responses.Add(ussdActionHttpResponse);
                                                    }

                                                }
                                            }
                                        }



                                    }
                                }


                                ussdScreen.UssdItems.Add(ussdAction);
                                _actions.Add(ussdAction);
                                Console.WriteLine("added action {0}", ussdAction.Id);
                            }

                            #endregion

                        }

                        if (ussdElement.Attributes["main"] != null
                            )
                        {
                            var mainString = ussdElement.Attributes["main"].InnerText;

                            bool isMain;

                            if (bool.TryParse(mainString, out isMain))
                            {
                                ussdScreen.Main = isMain;
                            }
                        }

                        _screens.Add(ussdScreen);

                        Console.WriteLine("{0} screens", _screens.Count);
                    }
                   
                    #endregion

                }
            }

            if (_screens.Count == 0)
            {
                throw new Exception("no screens");
            }

            if (!_screens.Any(s=>s.Main))
            {
                throw new Exception("no main attribute on any of the screens. Please include main=\"true\" as attribute on your main screen element");
            }
        }
Esempio n. 2
0
        private void ShowScreen(UssdScreen firstScreen)
        {
            
            foreach (var ussdItem in firstScreen.UssdItems)
            {

#region Menu
                if (ussdItem.GetType() == typeof (UssdMenu))
                {
                    var ussdMenu = (UssdMenu) ussdItem;

                    if (!string.IsNullOrEmpty(ussdMenu.BindAction.Name)) //bindaction is priority now
                    {

                        //menu action is not always a UssdAction
                        if (ussdMenu.BindAction.NavType == UssdNavigatorTypes.ToScript)
                        {
                            var script = _scripts.FirstOrDefault(s => s.Id == ussdMenu.BindAction.Name);

                            if (script == null)
                            {
                                throw new Exception($"script with ID {ussdMenu.BindAction.Name} not found");
                            }

                            ScriptOutput scriptResult;
                            try
                            {
                                var scriptOptions = ScriptOptions.Default.
                                    WithReferences(_scriptAssemblies)
                                    .WithImports(_scriptImports)
                                    .WithSourceResolver(new SourceFileResolver(new[] { "" },
                                        AppDomain.CurrentDomain.BaseDirectory));

                                dynamic eo2 = null;
                                var eo = new ExpandoObject();

                                var eoColl = (ICollection<KeyValuePair<string, dynamic>>)eo;

                                var scriptContent = script.Content;
                                if (!string.IsNullOrEmpty(script.BindFrom))
                                {
                                    //a hack to ensure that the proper class instance name is called instead of dev's "user.name"
                                    scriptContent = scriptContent.Replace(script.BindFrom + ".", "BindFrom.");

                                    var bindFromDict = _boundModels[script.BindFrom];

                                    foreach (var kvp in bindFromDict)
                                    {
                                        eoColl.Add(new KeyValuePair<string, dynamic>(kvp.Key, kvp.Value));
                                    }
                                   // eo2 = bindFromDict;
                                }

                                dynamic eoDynamic = eo;
                              

                                scriptResult = CSharpScript.EvaluateAsync<ScriptOutput>(scriptContent, scriptOptions, new ScriptInput(new LussdRequestContext(), eoDynamic), typeof(ScriptInput)).Result;

                                if (!string.IsNullOrEmpty(script.BindTo))
                                {
                                    //var sr = JsonConvert.SerializeObject(scriptResult.Response);

                                    //var dictionaryOfScriptResult =JsonConvert.DeserializeObject<Dictionary<string, string>>(sr);

                                    _boundModels[script.BindTo] = scriptResult.Response;

                                }
                                if (!string.IsNullOrEmpty(scriptResult.NextScreen))
                                {
                                    var nextScreen = _screens.FirstOrDefault(s => s.Id == scriptResult.NextScreen);
                                    if (nextScreen == null)
                                    {
                                        throw new Exception($"unknown screen \"{scriptResult.NextScreen}\"");
                                    }
                                    ShowScreen(nextScreen);
                                    return;
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new Exception(ex.Message);
                            }
                        }
                        if (ussdMenu.BindAction.NavType == UssdNavigatorTypes.ToScreen)
                        {
                            var screenToNavigate = _screens.FirstOrDefault(s => s.Id == ussdMenu.BindAction.Name);

                            if (screenToNavigate == null)
                            {
                                throw new Exception($"screen id \"{ussdMenu.BindAction.Name}\" does not exist");
                            }

                            ShowScreen(screenToNavigate);
                        }

                        if (ussdMenu.BindAction.NavType == UssdNavigatorTypes.ToAction)
                        {
                            UssdAction actionItem = _actions.FirstOrDefault(a => a.Id == ussdMenu.BindAction.Name);

                            if (actionItem == null)
                            {
                                throw new Exception($"undefined action {ussdMenu.BindAction.Name}");
                            }

                            if (actionItem.Http.Cycle == UssdActionCycle.Once)
                            {
                                //todo: might throw an error
                                if (_boundModels[actionItem.BindTo.Name].Any()) //todo: this might not be so necessary, cos a developer may just want to call an API, no binding
                                {

                                }
                                else
                                {


                                    var actionResponse = GrabActionResponse(actionItem).Result;


                                    var bdict = new Dictionary<string, dynamic>();

                                    foreach (var property in actionResponse.Item2.Properties())
                                    {
                                        bdict.Add(property.Name, property.Value);
                                    }

                                    ussdMenu.BindAction.Value = bdict;

                                    _boundModels[actionItem.BindTo.Name] = actionResponse.Item2;

                                    Console.WriteLine("HTTP response {0}", actionResponse.Item1);

                                    if (actionItem.Http.Responses.Any())
                                    {
                                        var navigatorLabel =
                                            actionItem.Http.Responses.FirstOrDefault(
                                                r => r.Value == (int)actionResponse.Item1);

                                        if (navigatorLabel != null)
                                        {
                                            //todo: we need to discourage "ussd:action" for now....
                                            ShowScreen(
                                                _screens.FirstOrDefault(
                                                    s => s.Id == navigatorLabel.Goto.Id));
                                            return;
                                        }
                                    }
                                    //no replacement occured....HTTP failed.

                                }

                            }
                            else if (actionItem.Http.Cycle == UssdActionCycle.Always)
                            {


                                var actionResponse = GrabActionResponse(actionItem).Result;


                                var bdict = new Dictionary<string, dynamic>();

                                foreach (var property in actionResponse.Item2.Properties())
                                {
                                    bdict.Add(property.Name, property.Value);
                                }

                                ussdMenu.BindAction.Value = bdict;

                                _boundModels[actionItem.BindTo.Name] = actionResponse.Item2;

                                Console.WriteLine("HTTP response {0}", actionResponse.Item1);

                                if (actionItem.Http.Responses.Any())
                                {
                                    var navigatorLabel =
                                        actionItem.Http.Responses.FirstOrDefault(
                                            r => r.Value == (int)actionResponse.Item1);

                                    if (navigatorLabel != null)
                                    {
                                        //todo: we need to discourage "ussd:action" for now....
                                        ShowScreen(
                                            _screens.FirstOrDefault(
                                                s => s.Id == navigatorLabel.Goto.Id));
                                        return;
                                    }
                                }
                            }
                        }

                    }

                    var model = _boundModels.Keys.FirstOrDefault(k => k == ussdMenu.BindFrom.Name);

                    if (model == null)
                    {
                        Console.WriteLine(ussdMenu.Title.Trim());
                    }
                    else
                    {
                        var title = ussdMenu.Title.Trim();
                        var boundModel = _boundModels[ussdMenu.BindFrom.Name];

                        title = title.ToUssdString();

                        dynamic dynamicExpression = boundModel;
                        //end

                        try
                        {
                            var sContent = "var " + ussdMenu.BindFrom.Name + " = LussdDynamicExpression; " + "\r\n return $\"" + title + "\";";
                            var scriptOptions = ScriptOptions.Default.
                                        WithReferences(_scriptAssemblies)
                                        .WithImports(_scriptImports)
                                        .WithSourceResolver(new SourceFileResolver(new[] { "" },
                                            AppDomain.CurrentDomain.BaseDirectory));
                            var scriptResult = CSharpScript.EvaluateAsync<string>(sContent, scriptOptions, new LussdDynamicExpressionObject { LussdDynamicExpression = dynamicExpression }, typeof(LussdDynamicExpressionObject)).Result;

                            Console.WriteLine(scriptResult);
                        }
                        catch (Exception exception)
                        {
                            throw new Exception(exception.Message);
                        }

                        
                    }


                    foreach (var ussdMenuOption in ussdMenu.Options)
                    {
                        //todo: check for variable expressions
                        if (model != null)
                        {
                            var title = ussdMenuOption.Text;
                            var boundModel = _boundModels[ussdMenu.BindFrom.Name];

                            title = title.ToUssdString();

                            dynamic dynamicExpression = boundModel;
                            //end

                            try
                            {
                                var sContent = "var " + ussdMenu.BindFrom.Name + " = LussdDynamicExpression; " + "\r\n return $\"" + title + "\";";
                                var scriptOptions = ScriptOptions.Default.
                                            WithReferences(_scriptAssemblies)
                                            .WithImports(_scriptImports)
                                            .WithSourceResolver(new SourceFileResolver(new[] { "" },
                                                AppDomain.CurrentDomain.BaseDirectory));
                                var scriptResult = CSharpScript.EvaluateAsync<string>(sContent, scriptOptions, new LussdDynamicExpressionObject { LussdDynamicExpression = dynamicExpression }, typeof(LussdDynamicExpressionObject)).Result;

                                Console.WriteLine(scriptResult);
                            }
                            catch (Exception exception)
                            {
                                throw new Exception(exception.Message);
                            }
                        }
                        else
                        {
                            Console.WriteLine(ussdMenuOption.Text);
                        }
                      
                    }

                    var resp = Console.ReadLine();
                    while (true)
                    {
                        if (ussdMenu.Options.FirstOrDefault(s => s.Value == resp) != null)
                        {
                            break;
                        }
                        resp = Console.ReadLine();
                    }

                    var menuOption = ussdMenu.Options.FirstOrDefault(s => s.Value == resp);
                    if (menuOption.OnSelect.NavType== UssdNavigatorTypes.ToScript)
                    {
                        var script = _scripts.FirstOrDefault(s => s.Id == menuOption.OnSelect.Id);

                        if (script == null)
                        {
                            throw new Exception($"script with ID {menuOption.OnSelect.Id} not found");
                        }

                        ScriptOutput scriptResult;
                        try
                        {
                            var scriptOptions = ScriptOptions.Default.
                                WithReferences(_scriptAssemblies)
                                .WithImports(_scriptImports)
                                .WithSourceResolver(new SourceFileResolver(new[] { "" },
                                    AppDomain.CurrentDomain.BaseDirectory));


                           // var eo = new ExpandoObject();
                            dynamic eo2 = null;
                          //  var eoColl = (ICollection<KeyValuePair<string, dynamic>>)eo;

                            if (!string.IsNullOrEmpty(script.BindFrom))
                            {
                                var bindFromDict = _boundModels[script.BindFrom];

                                
                                //foreach (var kvp in bindFromDict)
                                //{
                                //    eoColl.Add(new KeyValuePair<string, dynamic>(kvp.Key, kvp.Value));
                                //}

                                eo2 = bindFromDict;
                            }

                            dynamic eoDynamic = eo2;
                            var scriptContent = script.Content;
                            //a hack to ensure that the proper class instance name is called instead of dev's "user.name"
                            scriptContent = scriptContent.Replace(script.BindFrom + ".", "BindFrom.");

                            scriptResult = CSharpScript.EvaluateAsync<ScriptOutput>(scriptContent, scriptOptions, new ScriptInput(new LussdRequestContext(), eoDynamic), typeof(ScriptInput)).Result;

                            if (!string.IsNullOrEmpty(script.BindTo))
                            {
                                //var sr = JsonConvert.SerializeObject(scriptResult.Response);

                               // var dictionaryOfScriptResult =
                                 //   JsonConvert.DeserializeObject<Dictionary<string, string>>(sr);

                                _boundModels[script.BindTo] = scriptResult.Response;

                            }
                            if (!string.IsNullOrEmpty(scriptResult.NextScreen))
                            {
                                var nextScreen = _screens.FirstOrDefault(s => s.Id == scriptResult.NextScreen);
                                if (nextScreen == null)
                                {
                                    throw new Exception($"unknown screen \"{scriptResult.NextScreen}\"");
                                }
                                ShowScreen(nextScreen);
                                return;
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new Exception(ex.Message);
                        }
                    }
                    if (menuOption.OnSelect.NavType==UssdNavigatorTypes.ToAction)
                    {
                 
                        var actionItem = _actions.FirstOrDefault(a => a.Id == menuOption.OnSelect.Id);
                        if (actionItem == null)
                        {
                            throw new Exception($"undefined action {ussdMenu.BindAction.Name}");
                        }

                        if (actionItem.Http.Cycle == UssdActionCycle.Once)
                        {
                            if (_boundModels[actionItem.BindTo.Name].Any()) //todo: this might not be so necessary, cos a developer may just want to call an API, no binding
                            {

                            }
                            else
                            {

                                var actionResponse = GrabActionResponse(actionItem).Result;


                                var bdict = new Dictionary<string, dynamic>();

                                foreach (var property in actionResponse.Item2.Properties())
                                {
                                    bdict.Add(property.Name, property.Value);
                                }

                                ussdMenu.BindAction.Value = bdict;

                                _boundModels[actionItem.BindTo.Name] = actionResponse.Item2;

                                Console.WriteLine("HTTP response {0}", actionResponse.Item1);

                                if (actionItem.Http.Responses.Any())
                                {
                                    var navigatorLabel =
                                        actionItem.Http.Responses.FirstOrDefault(
                                            r => r.Value == (int)actionResponse.Item1);

                                    if (navigatorLabel != null)
                                    {
                                        //todo: we need to discourage "ussd:action" for now....
                                        ShowScreen(
                                            _screens.FirstOrDefault(
                                                s => s.Id == navigatorLabel.Goto.Id));
                                        return;
                                    }
                                }
        

                            }

                        }
                        else if (actionItem.Http.Cycle == UssdActionCycle.Always)
                        {
                         
                            //should always do fresh bind
                                var actionResponse = GrabActionResponse(actionItem).Result;


                            var bdict = new Dictionary<string, dynamic>();

                            foreach (var property in actionResponse.Item2.Properties())
                            {
                                bdict.Add(property.Name, property.Value);
                            }

                            ussdMenu.BindAction.Value = bdict;

                                _boundModels[actionItem.BindTo.Name] = actionResponse.Item2;

                                Console.WriteLine("HTTP response {0}", actionResponse.Item1);

                                if (actionItem.Http.Responses.Any())
                                {
                                    var navigatorLabel =
                                        actionItem.Http.Responses.FirstOrDefault(
                                            r => r.Value == (int)actionResponse.Item1);

                                    if (navigatorLabel != null)
                                    {
                                        //todo: we need to discourage "ussd:action" for now....
                                        ShowScreen(
                                            _screens.FirstOrDefault(
                                                s => s.Id == navigatorLabel.Goto.Id));
                                        return;
                                    }
                                }

                        }
                    }
                    if (menuOption.OnSelect.NavType== UssdNavigatorTypes.ToScreen)
                    {
                       // Console.WriteLine("navigating to {0}", menuOption.OnSelect.UssdScreen.Id);
                        var screenToNavigate = _screens.FirstOrDefault(s => s.Id == menuOption.OnSelect.UssdScreen.Id);

                        if (screenToNavigate == null)
                        {
                            throw new Exception($"screen id \"{menuOption.OnSelect.UssdScreen.Id}\" does not exist");
                        }

                        ShowScreen(screenToNavigate);
                        
                    }
                  
                }

                #endregion Menu

#region Text
                if (ussdItem.GetType() == typeof (UssdText))
                {
                    var ussdText = (UssdText) ussdItem;

                    if (!string.IsNullOrEmpty(ussdText.BindAction.Name))
                    {

                        if (ussdText.BindAction.NavType== UssdNavigatorTypes.ToScreen)
                        {
                            var screenToNavigate = _screens.FirstOrDefault(s => s.Id == ussdText.BindAction.Name);

                            if (screenToNavigate == null)
                            {
                                throw new Exception($"screen id \"{ussdText.BindAction.Name}\" does not exist");
                            }

                            ShowScreen(screenToNavigate);
                        }
                        if (ussdText.BindAction.NavType == UssdNavigatorTypes.ToScript)
                        {
                            var script = _scripts.FirstOrDefault(s => s.Id == ussdText.BindAction.Name);

                            if (script == null)
                            {
                                throw new Exception($"script with ID {ussdText.BindAction.Name} not found");
                            }

                            ScriptOutput scriptResult;
                            try
                            {
                                var scriptOptions = ScriptOptions.Default.
                                    WithReferences(_scriptAssemblies)
                                    .WithImports(_scriptImports)
                                    .WithSourceResolver(new SourceFileResolver(new[] { "" },
                                        AppDomain.CurrentDomain.BaseDirectory));


                                //var eo = new ExpandoObject();
                             

                                //var eoColl = (ICollection<KeyValuePair<string, dynamic>>)eo;

                                var scriptContent = script.Content;
                                dynamic eo2 = null;
                                if (!string.IsNullOrEmpty(script.BindFrom))
                                {
                                    //a hack to ensure that the proper class instance name is called instead of dev's "user.name"
                                    scriptContent = scriptContent.Replace(script.BindFrom + ".", "BindFrom.");


                                    var bindFromDict = _boundModels[script.BindFrom];

                                    //foreach (var kvp in bindFromDict)
                                    //{
                                    //    eoColl.Add(new KeyValuePair<string, dynamic>(kvp.Key, kvp.Value));
                                    //}

                                    eo2 = bindFromDict;
                                }

                                dynamic eoDynamic = eo2;
                               
                                scriptResult = CSharpScript.EvaluateAsync<ScriptOutput>(scriptContent, scriptOptions, new ScriptInput(new LussdRequestContext(), eoDynamic), typeof(ScriptInput)).Result;

                                if (!string.IsNullOrEmpty(script.BindTo))
                                {
                                    //var sr = JsonConvert.SerializeObject(scriptResult.Response);

                                    //var dictionaryOfScriptResult =
                                    //    JsonConvert.DeserializeObject<Dictionary<string, string>>(sr);

                                    _boundModels[script.BindTo] = scriptResult.Response;

                                }
                                if (!string.IsNullOrEmpty(scriptResult.NextScreen))
                                {
                                    var nextScreen = _screens.FirstOrDefault(s => s.Id == scriptResult.NextScreen);
                                    if (nextScreen == null)
                                    {
                                        throw new Exception($"unknown screen \"{scriptResult.NextScreen}\"");
                                    }
                                    ShowScreen(nextScreen);
                                    return;
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new Exception(ex.Message);
                            }
                        }
                        if (ussdText.BindAction.NavType == UssdNavigatorTypes.ToAction)
                        {
                            var actionItem = _actions.FirstOrDefault(a => a.Id == ussdText.BindAction.Name);


                            if (actionItem == null)
                            {
                                throw new Exception($"undefined action {ussdText.BindAction.Name}");
                            }

                            if (actionItem.Http.Cycle == UssdActionCycle.Once)
                            {
                                if (_boundModels[actionItem.BindTo.Name].Any())
                                {

                                }
                                else
                                {

                                    var actionResponse = GrabActionResponse(actionItem).Result;

                                    var bdict = new Dictionary<string, dynamic>();

                                    foreach (var property in actionResponse.Item2.Properties())
                                    {
                                        bdict.Add(property.Name, property.Value);
                                    }

                                    ussdText.BindAction.Value =bdict;

                                    _boundModels[actionItem.BindTo.Name] = actionResponse.Item2;



                                    Console.WriteLine("HTTP response {0}", actionResponse.Item1);
                                    //no replacement occured....HTTP failed.
                                    if (actionItem.Http.Responses.Any())
                                    {
                                        var navigatorLabel =
                                            actionItem.Http.Responses.FirstOrDefault(
                                                r => r.Value == (int)actionResponse.Item1);

                                        if (navigatorLabel != null)
                                        {
                                            //todo: check which type of label it is
                                            ShowScreen(
                                                _screens.FirstOrDefault(
                                                    s => s.Id == navigatorLabel.Goto.Id));
                                            return;
                                        }
                                    }

                                }

                            }
                            else if (actionItem.Http.Cycle == UssdActionCycle.Always)
                            {


                                var actionResponse = GrabActionResponse(actionItem).Result;


                                var bdict = new Dictionary<string, dynamic>();

                                foreach (var property in actionResponse.Item2.Properties())
                                {
                                    bdict.Add(property.Name, property.Value);
                                }

                                ussdText.BindAction.Value = bdict;

                                _boundModels[actionItem.BindTo.Name] = actionResponse.Item2;

                                Console.WriteLine("HTTP response {0}", actionResponse.Item1);

                                if (actionItem.Http.Responses.Any())
                                {
                                    var navigatorLabel =
                                        actionItem.Http.Responses.FirstOrDefault(
                                            r => r.Value == (int)actionResponse.Item1);

                                    if (navigatorLabel != null)
                                    {
                                        //todo: we need to discourage "ussd:action" for now....
                                        ShowScreen(
                                            _screens.FirstOrDefault(
                                                s => s.Id == navigatorLabel.Goto.Id));
                                        return;
                                    }
                                }




                            }
                        }
                        

                    }

                    var model = _boundModels.Keys.FirstOrDefault(k => k == ussdText.BindFrom.Name);

                    if (model == null)
                    {
                        Console.WriteLine(ussdText.Text.Trim());
                    }
                    else
                    {
                       

                        var title = ussdText.Text.Trim();
                        var boundModel = _boundModels[ussdText.BindFrom.Name];

                        title = title.ToUssdString();

                        //var eo = new ExpandoObject();

                        //var eoColl = (ICollection<KeyValuePair<string, dynamic>>)eo;
                        
                        //foreach (var kvp in boundModel)
                        //{
                        //    eoColl.Add(new KeyValuePair<string, dynamic>(kvp.Key, kvp.Value));
                        //}

                        dynamic dynamicExpression = boundModel;
                        //end

                        try
                        {
                            var sContent = "var " + ussdText.BindFrom.Name + " = LussdDynamicExpression; " + "\r\n return $\"" + title + "\";";
                            var scriptOptions = ScriptOptions.Default.
                                        WithReferences(_scriptAssemblies)
                                        .WithImports(_scriptImports)
                                        .WithSourceResolver(new SourceFileResolver(new[] { "" },
                                            AppDomain.CurrentDomain.BaseDirectory));
                            var scriptResult = CSharpScript.EvaluateAsync<string>(sContent, scriptOptions, new LussdDynamicExpressionObject { LussdDynamicExpression = dynamicExpression }, typeof(LussdDynamicExpressionObject)).Result;

                            Console.WriteLine(scriptResult);
                        }
                        catch (Exception exception)
                        {
                            throw new Exception(exception.Message);
                        }
                    
                        
                    }
                   
                }

                #endregion Text

#region Form
                if (ussdItem.GetType() == typeof (UssdForm))
                {
                    var ussdForm = (UssdForm) ussdItem;

                    Console.WriteLine(ussdForm.Title.Trim()); //todo: parse the title, include bindfrom for form

                    foreach (var ussdFormInput in ussdForm.Inputs)
                    {


                        Console.WriteLine(ussdFormInput.Display);

                        if (ussdFormInput.IsList)
                        {
                            var listItem = _formLists.FirstOrDefault(l => l.Id == ussdFormInput.ListId);

                            if (listItem == null)
                            {
                                throw new Exception($"list with id {ussdFormInput.ListId} not found");
                            }

                            if (listItem.HasRepeater)
                            {
                                
                                var script = _scripts.FirstOrDefault(s => s.Id == listItem.RepeaterScriptId);

                                if (script == null)
                                {
                                    throw new Exception($"script with ID {listItem.RepeaterScriptId} not found");
                                }


                                ScriptOutput scriptResult;
                                try
                                {
                                    var scriptOptions = ScriptOptions.Default.
                                        WithReferences(_scriptAssemblies)
                                        .WithImports(_scriptImports)
                                        .WithSourceResolver(new SourceFileResolver(new[] {""},
                                            AppDomain.CurrentDomain.BaseDirectory));


                                    var eo = new ExpandoObject();

                                    var eoColl = (ICollection<KeyValuePair<string, dynamic>>)eo;

                                    foreach (var kvp in ussdForm.BindTo.Value)
                                    {
                                        //eoColl.Add(new KeyValuePair<string, dynamic>(kvp.Key, kvp.Value));
                                        eoColl.Add(new KeyValuePair<string, dynamic>(kvp.Key, kvp.Value));
                                    }

                                    dynamic eoDynamic = eo;
                                    var scriptContent = script.Content;

                                    //a hack to ensure that the proper class instance name is called instead of dev's "user.name"
                                    scriptContent = scriptContent.Replace(script.BindFrom + ".", "BindFrom.");

                                    scriptResult = CSharpScript.EvaluateAsync<ScriptOutput>(scriptContent,scriptOptions,new ScriptInput(new LussdRequestContext(), eoDynamic),typeof(ScriptInput)).Result;
                                }
                                catch (Exception ex)
                                {
                                    throw new Exception(ex.Message);
                                }


                                listItem.Choices.Clear();
                                if (scriptResult.Response==null)
                                {
                                    throw new Exception("List response is null");
                                }
                                foreach (var item in scriptResult.Response)
                                {
                                    listItem.Choices.Add(new UssdFormListChoice
                                    {
                                        Value = item.value,
                                        Selector = item.selector,
                                        Text = item.text
                                    });
                                }
                            }

                            listItem.Choices.ForEach(Console.WriteLine);
                        }

                        if (ussdFormInput.Required)
                        {
                            while (true)
                            {
                             
                                var resp = Console.ReadLine();

                                if (!string.IsNullOrEmpty(resp))
                                {
                                    ussdForm.BindTo.Value[ussdFormInput.Id] = resp;
                                    break;
                                }
                            }
                        }
                        if (ussdFormInput.Type == "number")
                        {
                            var val = ussdForm.BindTo.Value[ussdFormInput.Id];
                            while (true)
                            {


                                if (IsNumber(val))
                                {
                                    ussdForm.BindTo.Value[ussdFormInput.Id] = val;
                                    break;
                                }
                                Console.WriteLine("INCORRECT INPUT (NUMBERS ONLY)- " + ussdFormInput.Display);
                                val = Console.ReadLine();
                            }
                        }
                        if (ussdFormInput.Type == "money")
                        {
                            var val = ussdForm.BindTo.Value[ussdFormInput.Id];
                            while (true)
                            {

                                decimal money;
                                if (decimal.TryParse(val, out money))
                                {
                                    ussdForm.BindTo.Value[ussdFormInput.Id] = val;
                                    break;
                                }
                                Console.WriteLine("INCORRECT INPUT (MONEY VALUE ONLY)- " + ussdFormInput.Display);
                                val = Console.ReadLine();
                            }
                        }
                        if (ussdFormInput.IsList)
                        {
                            var listItem = _formLists.FirstOrDefault(l => l.Id == ussdFormInput.ListId);

                            if (listItem==null)
                            {
                                throw new Exception($"list with id {ussdFormInput.ListId} not found");
                            }

                           
                            var val = ussdForm.BindTo.Value[ussdFormInput.Id];
                            while (true)
                            {
                                var listChoice = listItem.Choices.FirstOrDefault(s => s.Selector == val);
                                if (listChoice!=null)
                                {
                                    ussdForm.BindTo.Value[ussdFormInput.Id] = listChoice.Value;
                                    break;
                                }
                                Console.WriteLine("INCORRECT INPUT -- " + ussdFormInput.Display);
                                val = Console.ReadLine();
                            }
                        }

                        if (ussdFormInput.MaxLength.HasValue)
                        {
                            var val = ussdForm.BindTo.Value[ussdFormInput.Id];
                            while (true)
                            {


                                if (val.Length <= ussdFormInput.MaxLength.Value)
                                {
                                    ussdForm.BindTo.Value[ussdFormInput.Id] = val;
                                    break;
                                }
                                Console.WriteLine("INCORRECT INPUT (MAX LENGTH SHOULD BE " +
                                                  ussdFormInput.MaxLength.Value + ")- " + ussdFormInput.Display);
                                val = Console.ReadLine();
                            }
                        }

                    }

                    _boundModels[ussdForm.BindTo.Name] = ussdForm.BindTo.Value;

                    if (ussdForm.OnSubmit.NavType== UssdNavigatorTypes.ToScreen)
                    {
                      
                        var screenToNavigate = _screens.FirstOrDefault(s => s.Id == ussdForm.OnSubmit.UssdScreen.Id);

                        if (screenToNavigate == null)
                        {
                            throw new Exception($"screen id \"{ussdForm.OnSubmit.UssdScreen.Id}\" does not exist");
                        }

                        ShowScreen(screenToNavigate);
                    }
                    if (ussdForm.OnSubmit.NavType== UssdNavigatorTypes.ToScript)
                    {
                        
                        var script = _scripts.FirstOrDefault(s => s.Id == ussdForm.OnSubmit.Id);

                        if (script==null)
                        {
                            throw new Exception($"script with id {ussdForm.OnSubmit.Id} not found");
                        }
                        ScriptOutput scriptResult;
                        try
                        {
                            var scriptOptions = ScriptOptions.Default.
                                WithReferences(_scriptAssemblies).WithImports(_scriptImports);

                            var eo = new ExpandoObject();

                            var eoColl = (ICollection<KeyValuePair<string, dynamic>>)eo;

                            foreach (var kvp in ussdForm.BindTo.Value)
                            {
                                eoColl.Add(new KeyValuePair<string, dynamic>(kvp.Key, kvp.Value));
                            }

                            dynamic eoDynamic = eo;
                            var scriptContent = script.Content;
                            scriptContent = scriptContent.Replace(script.BindFrom + ".", "BindFrom.");

                            scriptResult = CSharpScript.EvaluateAsync<ScriptOutput>(scriptContent, scriptOptions,new ScriptInput(new LussdRequestContext(), eoDynamic),typeof(ScriptInput)).Result;


                            if (!string.IsNullOrEmpty(script.BindTo))
                            {
                                //var sr = JsonConvert.SerializeObject(scriptResult.Response);

                                //var dictionaryOfScriptResult =
                                //    JsonConvert.DeserializeObject<Dictionary<string, string>>(sr);

                                _boundModels[script.BindTo] = scriptResult.Response;

                            }
                            if (!string.IsNullOrEmpty(scriptResult.NextScreen))
                            {
                                var nextScreen = _screens.FirstOrDefault(s => s.Id == scriptResult.NextScreen);
                                if (nextScreen==null)
                                {
                                    throw new Exception($"unknown screen \"{scriptResult.NextScreen}\"");
                                }
                                ShowScreen(nextScreen);
                                return;
                            }

                        }
                        catch (Exception ex)
                        {
                            throw new Exception(ex.Message);
                        }

                    }

                    if (ussdForm.OnSubmit.NavType== UssdNavigatorTypes.ToAction)
                    {
                        var actionItem = _actions.FirstOrDefault(a => a.Id ==ussdForm.OnSubmit.Id);
                        if (actionItem == null)
                        {
                            throw new Exception($"undefined action {ussdForm.OnSubmit.Id}");
                        }

                        if (actionItem.Http.Cycle == UssdActionCycle.Once)
                        {
                            if (_boundModels[actionItem.BindTo.Name].Any()) //todo: this might not be so necessary, cos a developer may just want to call an API, no binding
                            {

                            }
                            else
                            {

                                var actionResponse = GrabActionResponse(actionItem).Result;


                                var bdict = new Dictionary<string, dynamic>();

                                foreach (var property in actionResponse.Item2.Properties())
                                {
                                    bdict.Add(property.Name, property.Value);
                                }

                                ussdForm.BindTo.Value = bdict;

                                _boundModels[actionItem.BindTo.Name] = actionResponse.Item2;

                                Console.WriteLine("HTTP response {0}", actionResponse.Item1);

                                if (actionItem.Http.Responses.Any())
                                {
                                    var navigatorLabel =
                                        actionItem.Http.Responses.FirstOrDefault(
                                            r => r.Value == (int)actionResponse.Item1);

                                    if (navigatorLabel != null)
                                    {
                                        //todo: we need to discourage "ussd:action" for now....
                                        //it might cause recursive actions
                                        ShowScreen(
                                            _screens.FirstOrDefault(
                                                s => s.Id == navigatorLabel.Goto.Id));
                                        return;
                                    }
                                }


                            }

                        }
                        else if (actionItem.Http.Cycle == UssdActionCycle.Always)
                        {

                            //should always do fresh bind
                            var actionResponse = GrabActionResponse(actionItem).Result;


                            var bdict = new Dictionary<string, dynamic>();

                            foreach (var property in actionResponse.Item2.Properties())
                            {
                                bdict.Add(property.Name, property.Value);
                            }
                            ussdForm.BindTo.Value = bdict;
                            

                            _boundModels[actionItem.BindTo.Name] = actionResponse.Item2;

                            Console.WriteLine("HTTP response {0}", actionResponse.Item1);

                            if (actionItem.Http.Responses.Any())
                            {
                                var navigatorLabel =
                                    actionItem.Http.Responses.FirstOrDefault(
                                        r => r.Value == (int)actionResponse.Item1);

                                if (navigatorLabel != null)
                                {
                                    //todo: we need to discourage "ussd:action" for now....
                                    ShowScreen(
                                        _screens.FirstOrDefault(
                                            s => s.Id == navigatorLabel.Goto.Id));
                                    return;
                                }
                            }

                        }
                    }
                    
                }

#endregion Form
            }


        }