static void Main(string[] args)
        {
            Log.ConsoleMode = true;

            int id = 0;
            var changeFiles = new List<ChangeFile>();

            var options = new GenDiffArgs();
            if (Parser.ParseArgumentsWithUsage(args, options))
            {
                if (!string.IsNullOrEmpty(options.CL))
                {
                    //     insert application code here
                    var changeList = (from change in db.ChangeLists
                                      where change.CL == options.CL
                                      select change).FirstOrDefault();
                    if (changeList != null)
                        id = changeList.Id;
                }

                if (!string.IsNullOrEmpty(options.file))
                {
                    var fileName = options.file.ToLower();
                    var files = (from file in db.ChangeFiles
                                       where file.ServerFileName.ToLower().Contains(fileName) &&
                                            (id == 0 || file.ChangeListId == id)
                                       select file);
                    foreach (var file in files)
                    {
                        changeFiles.Add(file);
                    }
                }

                if (options.fileId > 0 && options.revisionId < 0)
                {
                    var fileName = options.file.ToLower();
                    var files = (from file in db.ChangeFiles
                                 where file.Id == options.fileId &&
                                      (id == 0 || file.ChangeListId == id)
                                 select file);
                    foreach (var file in files)
                    {
                        changeFiles.Add(file);
                    }
                }
            }

            var userName = Environment.GetEnvironmentVariable("USERNAME").Trim();
            UserName = new UserName(userName, db);
            UserSettings = ReviewUtil.UserSettings(0, UserName.userName, db);

            if (changeFiles.Count > 0)
            {
                foreach (var changeFile in changeFiles)
                {
                    DiffRevision(changeFile.ChangeListId, changeFile.Id, changeFile.ReviewRevision - 1, force);
                }
            }
            else if (id != 0)
            {
                if (options.fileId != 0 && options.revisionId >= 0)
                {
                    DiffRevision(id, options.fileId, options.revisionId, force);
                }
                else
                {
                    var gen = new DiffGenerator(db);
                    gen.GenDiffFiles(id, UserName.userName, UserSettings, force);
                }
            }
            else
            {
                var gen = new DiffGenerator(db);

                var changelists = (from item in db.ChangeLists.AsNoTracking()
                                   where (item.Stage != (int)ChangeListStatus.Deleted)
                                   select item).OrderByDescending(x => x.TimeStamp);
                foreach (var item in changelists)
                {
                    Log.Info("Generate diff files for {0} {1}", item.CL, item.Id);
                    gen.GenDiffFiles(item.Id, UserName.userName, UserSettings, force);
                }
            }
        }
        protected ChangeListDto GetChangeListForFile(int id, string userName, int fileId, UserSettingsDto settings)
        {
            var changeList = GetChangeList(id);
            var changeFile = GetChangeFile(changeList, fileId);

            var dto = new ChangeListDto(changeList, true);
            var changeFileDto = dto.changeFiles.Find(item => item.id == fileId);
            if (changeFile == null || changeFile.ChangeList == null)
                throw new ApplicationException(string.Format("Error creating ChangeListDto model for change list id: {0} cl: {1} file: {2}", changeList.Id, changeList.CL, fileId));

            return new ChangeListDto(changeList, true);
        }
        /// <summary>
        ///     Computes and displays the diff between two distinct versions. Uses unix "diff" command to actually
        ///     produce the diff. This is relatively slow operation and involves spooling the data into two temp
        ///     files and running an external process.
        /// </summary>
        public string GenerateDiffFile(FileVersion left, FileVersion right, string clName, int baseReviewId, UserSettingsDto settings, bool force = false)
        {
            string outputFile = null;
            if (settings.diff.DefaultSettings)
            {
                outputFile = string.Format(@"{0}\{1}\{2}\{3}.{4}.htm", _diffFolderRoot, clName, left.FileId, left.Id, right.Id);
                if (File.Exists(outputFile))
                {
                    if (!force)
                        return outputFile;
                    File.Delete(outputFile);
                }
            }

            var useLeft = true;
            var useRight = true;
            var ignoreBase = false;

            if (left.Id == right.Id)
            {
                ignoreBase = true;
                var actionType = (SourceControlAction)Enum.ToObject(typeof(SourceControlAction), left.Action);
                switch (actionType)
                {
                    case SourceControlAction.Add:
                    case SourceControlAction.Branch:
                    case SourceControlAction.Edit:
                    case SourceControlAction.Integrate:
                    case SourceControlAction.Rename:
                    default:
                        useLeft = false;
                        break;

                    case SourceControlAction.Delete:
                        useRight = false;
                        break;
                }
            }

            using (var leftFile = useLeft ? SaveToTempFile(left, ignoreBase) : EmptyTempFile())
            using (var rightFile = useRight ? SaveToTempFile(right, ignoreBase) : EmptyTempFile())
            {
                if (leftFile == null || rightFile == null)
                    throw new ApplicationException(string.Format("Could not save temporary file for diffs. change list: {0} left file id: {1} right file id: {2}", clName, left.FileId, right.FileId));

                string stderr = null;
                string result = null;

                if (ConfigurationManager.AppSettings.LookupValue("preDiff", false) && settings.diff.preDiff)
                {
                    PreDiffFile(leftFile.FullName, rightFile.FullName);
                }

                result = DiffFiles(leftFile.FullName, rightFile.FullName, settings.diff.ignoreWhiteSpace, ref stderr);
                if (!string.IsNullOrEmpty(stderr))
                {
                    ErrorOut("Diff failed.");
                    ErrorOut(stderr);

                    return "Error Failed to generate diff. " + stderr;
                }

                using (var leftStream = new StreamCombiner(new StreamReader(leftFile.FullName)))
                using (var rightStream = new StreamCombiner(new StreamReader(rightFile.FullName)))
                using (var rawDiffStream = new StreamCombiner(result))
                {
                    var leftName = string.Format("{0}_{1}", Path.GetFileName(left.ChangeFile.ServerFileName), left.Id);
                    var rightName = string.Format("{0}_{1}", Path.GetFileName(right.ChangeFile.ServerFileName),
                                                  right.Id);

                    if (string.IsNullOrEmpty(outputFile))
                        outputFile = string.Format(@"{0}\{1}\{2}\{3}.{4}.delete.{5}.htm", _diffFolderRoot, clName, left.FileId, left.Id, right.Id, this._random.Next());

                    GenerateFileDiffView(left.ReviewRevision, right.ReviewRevision, settings,
                                                leftStream, left.Id, leftName,
                                                rightStream, right.Id, rightName,
                                                rawDiffStream, clName, outputFile);

                    GenerateDiffInfoFile(clName, left, right, baseReviewId);

                    return outputFile;
                }
            }
        }
        /// <summary>
        ///     Generates the diff view for two file revisions.
        /// </summary>
        private string GenerateFileDiffView(
            int baseRevision, int diffRevision, UserSettingsDto settings,
            StreamCombiner baseFile, int baseId, string baseHeader,
            StreamCombiner diffFile, int diffId, string diffHeader,
            StreamCombiner rawDiff, string fileName, string outputFile)
        {
            var baseEncoder = GetEncoderForFile(fileName);
            var diffEncoder = GetEncoderForFile(fileName);

            var baseFileInfo = new DiffFileInfo(baseFile, baseEncoder, baseId, BaseOrDiff.Base);
            var diffFileInfo = new DiffFileInfo(diffFile, diffEncoder, diffId, BaseOrDiff.Diff);

            // Line Stamp format
            var baseScriptIdPrefix = "Base-" + EncodeLinePrefix(baseRevision, diffRevision, baseId, diffId, baseId);
            var diffScriptIdPrefix = "Diff-" + EncodeLinePrefix(baseRevision, diffRevision, baseId, diffId, diffId);
            var edgePrefix = "Edge-" + EncodeLinePrefix(baseRevision, diffRevision, baseId, diffId, diffId);
            var rowGroups = new List<HtmlTableRowGroup>();
            var lastBaseLine = "1";
            var lastDiffLine = "1";

            foreach (var diffItem in DiffItem.EnumerateDifferences(rawDiff))
            {
                var atEnd = diffItem.BaseLineCount == int.MaxValue;

                var baseLines = new List<Line>();
                for (var i = 0; i < diffItem.BaseLineCount && baseFileInfo.MoveNextLine(); ++i)
                    baseLines.Add(new Line
                        {
                            LineNum = baseFileInfo.CurLineNum.ToString(CultureInfo.InvariantCulture),
                            LineText = baseFileInfo.CurLine
                        });

                var diffLines = new List<Line>();
                for (var i = 0; i < diffItem.DiffLineCount && diffFileInfo.MoveNextLine(); ++i)
                    diffLines.Add(new Line
                        {
                            LineNum = diffFileInfo.CurLineNum.ToString(CultureInfo.InvariantCulture),
                            LineText = diffFileInfo.CurLine
                        });

                var baseLinesLength = baseLines.Count();
                var diffLinesLength = diffLines.Count();

                // The end is the only case where the DiffInfo line counts may be incorrect. If there are in fact
                // zero lines then just continue, which should cause the foreach block to end and we'll continue
                // like the DiffItem never existed.
                if (atEnd && diffItem.DiffType == DiffType.Unchanged && baseLinesLength == 0 && diffLinesLength == 0)
                    continue;

                var rowGroup = new HtmlTableRowGroup(diffItem.DiffType);

                for (var i = 0; i < Math.Max(baseLinesLength, diffLinesLength); ++i)
                {
                    var line = new HtmlTableLine();
                    if (i < baseLinesLength)
                    {
                        line.Left = baseLines[i];
                        line.Left.LineText = baseFileInfo.Encoder.EncodeLine(baseLines[i].LineText, int.MaxValue,
                                                                             TabValue);
                        lastBaseLine = line.Left.LineNum;
                    }
                    else
                    {
                        // new diff code - add entry for empty line with old line #
                        var idx = i - baseLinesLength;
                        line.Left = new Line { Id = lastBaseLine + '_' + idx };
                    }

                    if (i < diffLinesLength)
                    {
                        line.Right = diffLines[i];
                        line.Right.LineText = diffFileInfo.Encoder.EncodeLine(diffLines[i].LineText, int.MaxValue,
                                                                             TabValue);
                        lastDiffLine = line.Right.LineNum;
                    }
                    else
                    {
                        // new diff code - add entry for empty line with old line #
                        var idx = i - diffLinesLength;
                        line.Right = new Line { Id = lastDiffLine + '_' + idx };
                    }

                    rowGroup.Lines.Add(line);
                }

                if (diffItem.DiffType == DiffType.Changed)
                {
                    if (settings.diff.intraLineDiff)
                        InterlineDiff(rowGroup, settings.diff.ignoreWhiteSpace);
                }

                rowGroups.Add(rowGroup);
            }

            baseEncoder.Dispose();
            diffEncoder.Dispose();

            var htmlLines = EncodeRowGroups(rowGroups, baseHeader, diffHeader, baseScriptIdPrefix, diffScriptIdPrefix,
                                       edgePrefix);
            if (!string.IsNullOrWhiteSpace(outputFile))
            {
                var folder = Path.GetDirectoryName(outputFile);
                Directory.CreateDirectory(folder);
                File.WriteAllLines(outputFile, htmlLines);
                Log.Info("Generated {0}", outputFile);
                return outputFile;
            }
            else
            {
                return string.Join(Environment.NewLine, htmlLines);
            }
        }
        public void GenDiffFiles(int id, string userName, UserSettingsDto settings, bool force = false)
        {
            var changeList = (from change in DataContext.ChangeLists
                              where change.Id == id
                              select change).FirstOrDefault();

            if (changeList == null)
            {
                // Log
                return;
            }
            var baseReviewId = GetBaseReviewId(userName, changeList.Id);

            foreach (var changeFile in changeList.ChangeFiles)
            {
                var fileVersions = changeFile.FileVersions.ToArray();

                if (fileVersions.Length == 1)
                {
                    GenerateDiffFile(fileVersions[0], fileVersions[0], changeList.CL, baseReviewId, settings, force);
                    continue;
                }

                var prev = 0;
                for (var idx = prev + 1; idx < fileVersions.Length; prev = idx++)
                {
                    var vid1 = fileVersions[prev].Id;
                    var vid2 = fileVersions[idx].Id;

                    var versionQuery = from vr in DataContext.FileVersions
                                       where vr.Id == vid1 || vr.Id == vid2
                                       select vr;
                    if (versionQuery.Count() == 2)
                    {
                        var leftRight = versionQuery.ToArray();
                        FileVersion left, right;
                        if (leftRight[0].Id == vid1)
                        {
                            left = leftRight[0];
                            right = leftRight[1];
                        }
                        else
                        {
                            left = leftRight[1];
                            right = leftRight[0];
                        }

                        GenerateDiffFile(left, right, changeList.CL, baseReviewId, settings, force);
                    }
                }
            }
        }