public static StatelessProjectResponse Server_HandleStatelessCodeCompletionRequest(StatelessProjectRequest request)
        {
            // Convert web request model to internal analysis model
            ProjectAnalysisRequest analysisRequest = new ProjectAnalysisRequest();
            analysisRequest.ProjectModel = ProjectModelConverters.FromDtoToModel(request.Project);
            if (request.CodeCompletionParameters != null)
            {
                analysisRequest.CodeCompletionParameters = new ProjectAnalysisCodeCompletionParameters()
                {
                    CtrlSpace = request.CodeCompletionParameters.CtrlSpace,
                    Offset = request.CodeCompletionParameters.Offset,
                    FileId = request.CodeCompletionParameters.FileId,
                    Line = request.CodeCompletionParameters.Line,
                    Column = request.CodeCompletionParameters.Column
                };
            }

            // Analyze!
            ProjectAnalysisResult analysisResult = NRefactoryUtils.RunFullProjectAnalysis(analysisRequest);

            // Convert analysis result model to web service model
            StatelessProjectResponse response = new StatelessProjectResponse();
            if (analysisResult.CompletionOptions != null)
            {
                response.CompletionOptions = analysisResult.CompletionOptions
                    .Select(CodeCompletionResultUtility.FromICompletionDataToFileCodeCompletionResult)
                    .ToArray();
                for (int i = 0, len = response.CompletionOptions.Length; i < len; i++)
                    response.CompletionOptions[i].Id = i;
                response.CompletionWord = analysisResult.CompletionWord;
                if (analysisResult.BestMatchToCompletionWord != null)
                    response.BestMatchToCompletionWord = response.CompletionOptions.FirstOrDefault(x => x.CompletionText == analysisResult.BestMatchToCompletionWord.CompletionText);
            }
            var allErrors = new List<FileParseResult>();
            foreach (var fileModel in analysisRequest.ProjectModel.GetFileDescendants())
            {
                allErrors.AddRange(fileModel.Parser.ErrorsAndWarnings
                                       .Select(x => new FileParseResult()
                                       {
                                           FileId = fileModel.Id,
                                           FileName = fileModel.Name,
                                           Line = x.Region.BeginLine,
                                           Column = x.Region.BeginColumn,
                                           Type = x.ErrorType,
                                           Message = x.Message
                                       }).ToArray());
            }
            response.Errors = allErrors.ToArray();
            response.MsElapsed = analysisResult.TimeElapsed.TotalMilliseconds; // string.Format("{0}", analysisResult.TimeElapsed.TotalMilliseconds);

            return response;
        }
        //// todo: eliminate this method later
        //private static void AugmentProjectModelWithAlgorithmBase(ProjectModel model)
        //{
        //    model.Children.Add(QcAlgorithmFileModel.Value);
        //}

        public static ProjectAnalysisResult RunFullProjectAnalysis(ProjectAnalysisRequest projectAnalysisRequest)
        {
            Stopwatch sw = Stopwatch.StartNew();

            ProjectAnalysisResult projectAnalysisResult = new ProjectAnalysisResult();
            ProjectModel projectModel = projectAnalysisRequest.ProjectModel;

            // todo: Ask Jared why QCAlgorithm is a partial class and why it's not included in "common"

#if false
            // ************************************************************************************
            // NOTE:  In order to get this project building cleanly, with minimal dependencies 
            // before checking into Github, I've removed all hard QuantConnect dependencies,
            // including the QCAlgorithm.cs embedded resource and the assembly references to 
            // QuantConnect.Algorithm.Interface

            // Augment the project model with the QCAgorithm base class
            // projectModel.Children.Add(QcAlgorithmFileModel.Value);
            
            // ************************************************************************************
#endif

            // Set up the project (if not already done)
            if (projectModel.ProjectContent == null)
            {
                projectModel.ProjectContent = new CSharpProjectContent();
                projectModel.ProjectContent = projectModel.ProjectContent.AddAssemblyReferences(QCReferences.Value);
            }

            // For each new file, we need to integrate it into the project content
            var fileModelsInProject = projectModel.GetFileDescendants().ToArray();
            foreach (var fileModelInProject in fileModelsInProject)
            {
                IntegrateFileModel(projectModel, fileModelInProject);
            }

            // We can return now if no code completion was requested
            if (projectAnalysisRequest.CodeCompletionParameters == null)
            {
                projectAnalysisResult.TimeElapsed = sw.Elapsed;
                return projectAnalysisResult;
            }

            // Now it's time to give attention specifically to the matter of resolving the code completion
            // options.  This, of course, requires a deeper analysis of the specified file...
            
            var codeCompletionParams = projectAnalysisRequest.CodeCompletionParameters;

            // Locate the file in the project
            ProjectFileModel fileModel = projectModel.FindFile(codeCompletionParams.FileId);
            if (fileModel == null)
                throw new Exception("Specified file does not exist in this project");
            

            // Create a TypeSystem.ICompilation that allows resolving within the project.
            var compilation = projectModel.ProjectContent.CreateCompilation();

            #region Resolve text cursor/caret location
            
            // The text cursor position is crucial to creating a properly-scoped type resolution context
            // so as to get relevant code completion suggestions.
            int textCursorOffset = 0;
            TextLocation textCursorLocation = new TextLocation(1, 1);
            ReadOnlyDocument doc = new ReadOnlyDocument(fileModel.Content);
            try
            {
                // if line and column aren't set, we'll assume that the cursor offset/index is set
                if (codeCompletionParams.Line == 0 && codeCompletionParams.Column == 0)
                {
                    textCursorOffset = codeCompletionParams.Offset;
                    if (textCursorOffset < 0) textCursorOffset = 0;
                    textCursorLocation = doc.GetLocation(textCursorOffset);
                }
                // if either line or column are invalid (i.e. <= 0), then we'll use offset 0 instead
                else if (codeCompletionParams.Line <= 0 || codeCompletionParams.Column <= 0)
                {
                    textCursorOffset = 0;
                }
                else
                {
                    textCursorLocation = new TextLocation(codeCompletionParams.Line, codeCompletionParams.Column);
                    textCursorOffset = doc.GetOffset(textCursorLocation);
                    codeCompletionParams.Offset = textCursorOffset;
                }
            }
            catch (Exception)
            {
                textCursorOffset = 0;
                textCursorLocation = new TextLocation(1, 1);
            }
            finally
            {
                projectAnalysisResult.Line = textCursorLocation.Line;
                projectAnalysisResult.Column = textCursorLocation.Column;
                projectAnalysisResult.Offset = textCursorOffset;
            }

            #endregion

            #region Create and Refine the type resolution context as much as possible based upon the cursor position

            var typeResolveContext = new CSharpTypeResolveContext(compilation.MainAssembly);
            // Constrain the resolve context by using scope
            typeResolveContext = typeResolveContext
                .WithUsingScope(fileModel.UnresolvedFile.GetUsingScope(textCursorLocation)
                .Resolve(compilation));

            var curDef = fileModel.UnresolvedFile.GetInnermostTypeDefinition(textCursorLocation);
            if (curDef != null)
            {
                var resolvedDef = curDef.Resolve(typeResolveContext).GetDefinition();
                typeResolveContext = typeResolveContext.WithCurrentTypeDefinition(resolvedDef);
                var curMember = resolvedDef.Members.FirstOrDefault(m => m.Region.Begin <= textCursorLocation && textCursorLocation < m.BodyRegion.End);
                if (curMember != null)
                {
                    typeResolveContext = typeResolveContext.WithCurrentMember(curMember);
                }
            }

            #endregion

            // The purpose of the rest of these steps is a little fuzzy in my mind...  
            // I'm still trying to understand them fully and if/why they're all needed.
            // It seems there is some redundancy here...

            var completionContext = new DefaultCompletionContextProvider(doc, fileModel.UnresolvedFile);

            #region Add Preprocessor Symbols??
            completionContext.AddSymbol("TEST");
            foreach (var sym in fileModel.SyntaxTree.ConditionalSymbols)
                completionContext.AddSymbol(sym);
            #endregion

            var completionDataFactory = new CodeCompletionDataFactory(new CSharpResolver(typeResolveContext));
            
            var completionEngine = new CSharpCompletionEngine(doc, completionContext, completionDataFactory, projectModel.ProjectContent, typeResolveContext);
            completionEngine.EolMarker = Environment.NewLine;
            completionEngine.FormattingPolicy = FormattingOptionsFactory.CreateMono();
            projectModel.CompletionEngine = completionEngine;

            // Attach contextual info to analysis result
            GetDocumentContext(projectAnalysisResult, textCursorOffset, doc);

            // Finally, generate completion data!
            var completionOptions = completionEngine.GetCompletionData(textCursorOffset, projectAnalysisRequest.CodeCompletionParameters.CtrlSpace).ToArray();

            projectAnalysisResult.CompletionOptions = completionOptions.OrderBy(x => x.CompletionText).ToArray();
            projectAnalysisResult.AutoCompleteEmptyMatch = completionEngine.AutoCompleteEmptyMatch;
            projectAnalysisResult.AutoSelect = completionEngine.AutoSelect;
            projectAnalysisResult.DefaultCompletionString = completionEngine.DefaultCompletionString;

            int startPos, wordLength;
            if (completionEngine.TryGetCompletionWord(textCursorOffset, out startPos, out wordLength))
            {
                //Debug.WriteLine("TryGetCompletionWord :: startpos:{0}  wordlength:{1}", startPos, wordLength);
                string completionWord = projectAnalysisResult.CompletionWord = doc.GetText(startPos, wordLength);

                if (!string.IsNullOrWhiteSpace(completionWord))
                {
                    var bestMatch = projectAnalysisResult.CompletionOptions
                        .FirstOrDefault(x => x.CompletionText.CompareTo(completionWord) >= 0);
                    projectAnalysisResult.BestMatchToCompletionWord = bestMatch;
                    //if (bestMatch != null)
                        //projectAnalysisResult.BestMatchToCompletionWord = bestMatch.CompletionText;
                }
            }

            projectAnalysisResult.TimeElapsed = sw.Elapsed;

            return projectAnalysisResult;
        }
        //private void ParseFile(ProjectFileViewModel fileViewModelToParse)
        //{
        //    var fileDto = ProjectMVVMConverters.FromViewModelToModel(fileViewModelToParse);

        //    MockWebServiceUtility.ParseFile(fileDto, response =>
        //    {
        //        if (response.Parse == null)
        //            return;

        //        var errors = response.Parse.Errors;
        //        //response.Parse.Errors
        //        if (errors != null)
        //        {
        //            Logger.AppendLine("{0} error(s) detected.", errors.Length);
        //            if (errors.Length > 0)
        //                foreach (var err in errors)
        //                    Logger.AppendLine("{0}: In file {1} ({2}, {3}): {4}", err.Type, err.FileName, err.Line, err.Column, err.Message);
        //        }

        //    });
        //}

        //private static FileOperationResponse DoAutoComplete(FileOperationRequest request)
        //{
        //    return AutocompleteServiceUtil.DoAutoComplete(request);
        //}


        private void AnalyzeProject2(bool ctrlSpace)
        {
            //ctrlSpace = true;
            var sw = Stopwatch.StartNew();

            var projectVm = (ProjectViewModel)DataContext;
            var fileVm = projectVm.SelectedProjectItem as ProjectFileViewModel;
            if (fileVm == null)
            {
                Logger.AppendLine("A file must be selected");
                return;
            }

            fileVm.CaretIndex = SelectedFileContent.CaretIndex;
            // Synchronize the view's content with the viewmodel (since by default this is only done AFTER the textbox loses focus)
            fileVm.Content = SelectedFileContent.Text;

            // update file content in local data store if not already done
            var projectModelRepo = EndpointHost.AppHost.TryResolve<IProjectModelRepository>();
            projectModelRepo.SaveFileContent(478, fileVm.ProjectId, fileVm.Id, fileVm.Content);


            var fileRequest = new FileOperationRequest()
            {
                UserId = 478, //projectVm..UserId,
                ProjectId = projectVm.ProjectId,
                FileId = fileVm.Id,
                CompleteCode = new FileCodeCompletionRequest()
                {
                    AutoComplete = true,
                    Offset = fileVm.CaretIndex,
                    LineNumber = fileVm.CaretLine,
                    ColumnNumber = fileVm.CaretColumn,
                    CtrlSpace = ctrlSpace
                },
                Parse = new FileParseRequest()
            };


            var fileResponse = AutocompleteServiceUtil.DoAutoComplete(fileRequest);

#if false

            var projectDto = ProjectMVVMConverters.FromViewModelToModel(projectVm);

            var projectModel = ProjectModelConverters.FromDtoToModel(projectDto);

            var analysisRequest = new ProjectAnalysisRequest()
            {
                ProjectModel = projectModel,
                CodeCompletionParameters = new ProjectAnalysisCodeCompletionParameters()
                {
                    CtrlSpace = ctrlSpace,
                    FileId = fileVm.Id,
                    Offset = fileVm.CaretIndex
                    //Line = request.CompleteCode.LineNumber,
                    //Column = request.CompleteCode.ColumnNumber,
                    //CtrlSpace = true // always true for now
                }
            };

            ProjectAnalysisResult analysisResult = NRefactoryUtils.RunFullProjectAnalysis(analysisRequest);

            //StatelessProjectResponse response = MockWebServiceUtility.Server_HandleStatelessCodeCompletionRequest(request);

            FileOperationResponse response = new FileOperationResponse();
            // Convert analysis result model to file operation response DTO
            if (analysisResult.CompletionOptions != null)
            {
                response.CodeCompletion = new FileCodeCompletionResponse();
                response.CodeCompletion.CompletionOptions = analysisResult.CompletionOptions
                    .Select(ProjectModelConverters.FromICompletionDataToFileCodeCompletionResult).ToArray();
                for (int i = 0, len = response.CodeCompletion.CompletionOptions.Length; i < len; i++)
                    response.CodeCompletion.CompletionOptions[i].Id = i;
                response.CodeCompletion.CompletionWord = analysisResult.CompletionWord;
                if (analysisResult.BestMatchToCompletionWord != null)
                    response.CodeCompletion.BestMatchToCompletionWord = response.CodeCompletion.CompletionOptions.FirstOrDefault(x => x.CompletionText == analysisResult.BestMatchToCompletionWord.CompletionText);
            }
            var allErrors = new List<FileParseResult>();
            foreach (var fileModel in analysisRequest.ProjectModel.GetFileDescendants())
            {
                allErrors.AddRange(fileModel.Parser.ErrorsAndWarnings
                                       .Select(x => new FileParseResult()
                                       {
                                           FileId = fileModel.Id,
                                           FileName = fileModel.Name,
                                           Line = x.Region.BeginLine,
                                           Column = x.Region.BeginColumn,
                                           Type = x.ErrorType,
                                           Message = x.Message
                                       }).ToArray());
            }
            response.ParseResults = allErrors.ToArray();
#endif

            //var jsonResponse = JsonConvert.SerializeObject(fileResponse, Formatting.Indented);
            //Logger.AppendLine(jsonResponse);

            // Summarize results...
            Logger.AppendLine("=========================================================");
            Logger.AppendLine("Project analysis completed in {0} ms", sw.ElapsedMilliseconds);

            if (fileResponse.CodeCompletion == null)
            {
                Logger.AppendLine("No Completion Results.");
                Logger.SetCodeCompletionOptions(null, null);
            }
            else
            {
                var codeCompletion = fileResponse.CodeCompletion;

                Logger.SetCodeCompletionOptions(codeCompletion.CompletionOptions, codeCompletion.BestMatchToCompletionWord);

                Logger.AppendLine("Completion Results...");
                Logger.AppendLine("  Input:  Line:{0}  Col:{1}  Offset:{2}", codeCompletion.Line, codeCompletion.Column, codeCompletion.Offset);
                Logger.AppendLine("  Context: \"{0}\" <cursor> \"{1}\"", codeCompletion.TextBeforeCursor, codeCompletion.TextAfterCursor);
                Logger.AppendLine("  {0} code completion option(s) generated.", codeCompletion.CompletionOptions.Length);

                // Try to find closest matching completion result
                if (string.IsNullOrWhiteSpace(codeCompletion.CompletionWord))
                {
                    Logger.AppendLine("  No code completion word detected.");
                }
                else
                {
                    if (codeCompletion.BestMatchToCompletionWord != null)
                    {
                        Logger.AppendLine("  Detected code completion word, \"{0}\", most closely matches completion option \"{1}\".",
                                          codeCompletion.CompletionWord, codeCompletion.BestMatchToCompletionWord.CompletionText);
                    }
                    else
                    {
                        Logger.AppendLine("  Detected code completion word: {0}", codeCompletion.CompletionWord);
                    }
                }
            }


            if (fileResponse.ParseResults != null)
            {
                Logger.AppendLine("{0} error(s) detected.", fileResponse.ParseResults.Length);
                if (fileResponse.ParseResults.Length > 0)
                    foreach (var err in fileResponse.ParseResults)
                        Logger.AppendLine("{0}: In file {1} ({2}, {3}): {4}", err.Type, err.FileName, err.Line, err.Column, err.Message);
            }

        }
        public static FileOperationResponse DoAutoComplete(FileOperationRequest request)
        {
            FileOperationResponse response = new FileOperationResponse();
            ProjectModel projectModel = null;
            Stopwatch sw = Stopwatch.StartNew();

            try
            {
                var projectModelRepo = EndpointHost.AppHost.TryResolve<IProjectModelRepository>();

                projectModel = projectModelRepo.GetProject(request.UserId, request.ProjectId);

                var analysisRequest = new ProjectAnalysisRequest()
                {
                    ProjectModel = projectModel,
                    CodeCompletionParameters = new ProjectAnalysisCodeCompletionParameters()
                    {
                        FileId = request.FileId,
                        Line = request.CompleteCode.LineNumber,
                        Column = request.CompleteCode.ColumnNumber,
                        Offset = request.CompleteCode.Offset,
                        CtrlSpace = request.CompleteCode.CtrlSpace
                    }
                };


                // Analyze!
                ProjectAnalysisResult analysisResult = NRefactoryUtils.RunFullProjectAnalysis(analysisRequest);


                // Convert analysis result model to file operation response DTO
                if (analysisResult.CompletionOptions != null)
                {
                    var codeCompletion = response.CodeCompletion = new FileCodeCompletionResponse();
                    codeCompletion.CompletionOptions = analysisResult.CompletionOptions
                        .Select(CodeCompletionResultUtility.FromICompletionDataToFileCodeCompletionResult).ToArray();
                    for (int i = 0, len = codeCompletion.CompletionOptions.Length; i < len; i++)
                        codeCompletion.CompletionOptions[i].Id = i;
                    codeCompletion.CompletionWord = analysisResult.CompletionWord;
                    if (analysisResult.BestMatchToCompletionWord != null)
                        codeCompletion.BestMatchToCompletionWord = codeCompletion.CompletionOptions.FirstOrDefault(x => x.CompletionText == analysisResult.BestMatchToCompletionWord.CompletionText);
                    // Record text around cursor
                    codeCompletion.TextBeforeCursor = analysisResult.CompletionContextBefore;
                    codeCompletion.TextAfterCursor = analysisResult.CompletionContextAfter;
                    // Record inputs
                    codeCompletion.Line = analysisResult.Line;
                    codeCompletion.Column = analysisResult.Column;
                    codeCompletion.Offset = analysisResult.Offset;
                }
                var allErrors = new List<FileParseResult>();
                foreach (var fileModel in analysisRequest.ProjectModel.GetFileDescendants())
                {
                    allErrors.AddRange(fileModel.Parser.ErrorsAndWarnings
                                           .Select(x => new FileParseResult()
                                           {
                                               FileId = fileModel.Id,
                                               FileName = fileModel.Name,
                                               Line = x.Region.BeginLine,
                                               Column = x.Region.BeginColumn,
                                               Type = x.ErrorType,
                                               Message = x.Message
                                           }).ToArray());
                }
                response.ParseResults = allErrors.ToArray();


                /*
                StatelessProjectResponse response = new StatelessProjectResponse();
                if (analysisResult.CompletionOptions != null)
                {
                    response.CompletionOptions = analysisResult.CompletionOptions
                        .Select(x => new FileCodeCompletionResult()
                        {
                            CompletionText = x.CompletionText,
                            DisplayText = x.DisplayText,
                            Description = x.Description,
                            OverloadCount = x.OverloadedData.Count(),
                            CompletionCategoryDisplayText = (x.CompletionCategory == null ? "" : x.CompletionCategory.DisplayText)
                        }).ToArray();
                    response.CompletionWord = analysisResult.CompletionWord;
                    if (analysisResult.BestMatchToCompletionWord != null)
                        response.BestMatchToCompletionWord = response.CompletionOptions.FirstOrDefault(x => x.CompletionText == analysisResult.BestMatchToCompletionWord.CompletionText);
                }
                var allErrors = new List<FileParseResult>();
                foreach (var fileModel in analysisRequest.ProjectModel.GetFileDescendants())
                {
                    allErrors.AddRange(fileModel.Parser.ErrorsAndWarnings
                                           .Select(x => new FileParseResult()
                                           {
                                               FileId = fileModel.Id,
                                               FileName = fileModel.Name,
                                               Line = x.Region.BeginLine,
                                               Column = x.Region.BeginColumn,
                                               Type = x.ErrorType,
                                               Message = x.Message
                                           }).ToArray());
                }
                response.Errors = allErrors.ToArray();
                response.MsElapsed = analysisResult.TimeElapsed.TotalMilliseconds; // string.Format("{0} ms", analysisResult.TimeElapsed.TotalMilliseconds);
                */
                response.Status.Success = true;
            }
            catch (Exception ex)
            {
                log.Error(ex.ToString(), ex);
                response.Status.SetError(ex);
            }
            finally
            {
                //response.CodeCompletion.MsElapsed = analysisResult.TimeElapsed.TotalMilliseconds; // string.Format("{0} ms", analysisResult.TimeElapsed.TotalMilliseconds);
                response.Status.MsElapsed = sw.ElapsedMilliseconds;
            }

            return response;
        }