context: using LinqToTTreeInterfacesLib;
  using LINQToTTreeLib.Expressions;
  using LINQToTTreeLib.QMFunctions;
  using LINQToTTreeLib.Statements;
  using LINQToTTreeLib.Tests;
  using LINQToTTreeLib.Variables;
  using Microsoft.VisualStudio.TestTools.UnitTesting;
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Linq.Expressions;
  
  namespace LINQToTTreeLib
  {
      /// <summary>This class contains parameterized unit tests for GeneratedCode</summary>
      [TestClass]
      public partial class GeneratedCodeTest
      {
          public void Pop(IStatement s, bool popPastLoop)
          {
              var gc = new GeneratedCode();
              int depth = gc.Depth;
              gc.Add(s);
  
              bool good = s is IBookingStatementBlock;
              try
              {
                  gc.Pop(popPastLoop);
                  Assert.AreEqual(depth, gc.Depth, "Depth isn't set correctly");
                  Assert.IsTrue(good, "booking statement");
              }
              catch (InvalidOperationException)
              {
                  Assert.IsFalse(good, "a booking statement");
              }
          }
  
          [TestMethod]
          public void AddQMFunc()
          {
              var gc = new GeneratedCode();
              Assert.AreEqual(0, gc.QMFunctions.Count(), "# of functions before add");
              gc.Add(new QMFuncSource());
              Assert.AreEqual(1, gc.QMFunctions.Count(), "# of functions before add");
          }
  
          /// <summary>
          /// Recursively add the items in...
          /// </summary>
          /// <param name="iBookingStatementBlock"></param>
          /// <returns></returns>
          private int CountStatements(IStatementCompound s)
          {
              int cnt = 0;
              foreach (var substatement in s.Statements)
              {
                  cnt++;
                  if (substatement is IStatementCompound)
                  {
                      cnt += CountStatements(substatement as IStatementCompound);
                  }
              }
  
              return cnt;
          }
  
          public class SimpleStatement : IStatement
          {
              public IEnumerable<string> CodeItUp()
              {
                  throw new NotImplementedException();
              }
  
  
              public void RenameVariable(string originalName, string newName)
              {
                  throw new NotImplementedException();
              }
  
  
              public bool TryCombineStatement(IStatement statement, ICodeOptimizationService opt)
              {
                  throw new NotImplementedException();
              }
  
  
              public IStatement Parent { get; set; }
          }
  
          public class CompoundStatement : IStatementCompound
          {
              private List<IStatement> _list = new List<IStatement>();
              public System.Collections.Generic.IEnumerable<IStatement> Statements
              {
                  get { return _list; }
              }
  
              public void Add(IStatement statement)
              {
                  _list.Add(statement);
              }
  
              public IEnumerable<string> CodeItUp()
              {
                  throw new NotImplementedException();
              }
  
              public void RenameVariable(string originalName, string newName)
              {
                  throw new NotImplementedException();
              }
  
  
              public IStatement Parent { get; set; }
  
  
              public void Remove(IStatement statement)
              {
                  throw new NotImplementedException();
              }
  
              public void AddBefore(IStatement statement, IStatement beforeThisStatement)
              {
                  throw new NotImplementedException();
              }
  
  
              public bool Combine(IEnumerable<IStatement> statements, IBookingStatementBlock parent, bool appendIfNoCombine = true, bool moveIfIdentical = false)
              {
                  throw new NotImplementedException();
              }
  
              public IEnumerable<IStatement> TryCombineStatement(IStatement statement, ICodeOptimizationService optimize)
              {
                  throw new NotImplementedException();
              }
  
  
              public IStatement CombineAndMark(IStatement statement, IBookingStatementBlock parent, bool appendIfNoCombine = true)
              {
                  throw new NotImplementedException();
              }
  
  
              bool IStatement.TryCombineStatement(IStatement statement, ICodeOptimizationService optimize)
              {
                  throw new NotImplementedException();
              }
  
  
              public bool IsBefore(IStatement first, IStatement second)
              {
                  throw new NotImplementedException();
              }
          }
  
          public class CompoundBookingStatement : CompoundStatement, IBookingStatementBlock
          {
              private List<IDeclaredParameter> _vlist = new List<IDeclaredParameter>();
              public void Add(IDeclaredParameter variableToDeclare, bool failifthere)
              {
                  _vlist.Add(variableToDeclare);
              }
  
              public void Remove(IDeclaredParameter var)
              {
                  _vlist.Remove(var);
              }
  
              public IEnumerable<IDeclaredParameter> DeclaredVariables
              {
                  get { return _vlist; }
              }
              public IEnumerable<IDeclaredParameter> AllDeclaredVariables
              {
                  get { return _vlist; }
              }
          }
  
          /// <summary>
          /// Seen in the wild... the internal scope stack isn't getting tracked.
          /// </summary>
          [TestMethod]
          public void CheckPopAndCurrentScopeWorkTogether()
          {
              GeneratedCode target = new GeneratedCode();
              IStatement s1 = new StatementInlineBlock();
              target.Add(s1);
              var depth = target.Depth;
              var s2 = new StatementInlineBlock();
              target.Add(s2);
              var firstTwoDown = target.CurrentScope;
  
              var s3 = new StatementInlineBlock();
              target.Add(s3);
  
              target.CurrentScope = firstTwoDown;
              // This pop should put us at the s1 level.
              target.Pop();
              Assert.AreEqual(depth, target.Depth);
          }
  
          [TestMethod]
          public void CheckDepthAndPopWorkTogether()
          {
              var target = new GeneratedCode();
              var s1 = new StatementInlineBlock();
              target.Add(s1);
              var s2 = new StatementInlineBlock();
              target.Add(s2);
              Assert.AreEqual(3, target.Depth);
              target.Pop();
              Assert.AreEqual(2, target.Depth);
          }
  
          [TestMethod]
          public void TestChangeScopeSpecificTopLevel()
          {
              GeneratedCode target = new GeneratedCode();
              IStatement s = new StatementInlineBlock();
  
              var currentS = target.CurrentScope;
  
              var deepestStatementLevel = TestUtils.GetDeepestStatementLevel(target);
              var deepestDeclarLevel = TestUtils.GetDeepestBookingLevel(target);
  
              var curVars = deepestDeclarLevel.DeclaredVariables.Count();
              var curStatements = deepestStatementLevel.Statements.Count();
              var v1 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
              target.Add(v1);
              target.Add(s);
  
              target.CurrentScope = currentS;
  
              var v2 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
              target.Add(v2);
              s.Parent = null;
              target.Add(s);
              Assert.AreEqual(curStatements + 2, deepestStatementLevel.Statements.Count(), "Scope reset, should always be two extra statements here!");
              Assert.AreEqual(curVars + 2, deepestDeclarLevel.DeclaredVariables.Count(), "Scope reset should have also reset where the variable was pointing");
          }
  
          [TestMethod]
          public void TestChangeScopeSpecificNextLevel()
          {
              GeneratedCode target = new GeneratedCode();
              target.Add(new StatementInlineBlock());
              IStatement s = new StatementInlineBlock();
  
              var deepestStatementLevel = TestUtils.GetDeepestStatementLevel(target);
              var deepestDeclarLevel = TestUtils.GetDeepestBookingLevel(target);
  
              var currentS = target.CurrentScope;
  
              var curVars = deepestDeclarLevel.DeclaredVariables.Count();
              var curStatements = deepestStatementLevel.Statements.Count();
              var v1 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
              target.Add(v1);
              target.Add(s);
  
              target.CurrentScope = currentS;
  
              var v2 = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int));
              target.Add(v2);
              s.Parent = null;
              target.Add(s);
              Assert.AreEqual(curStatements + 2, deepestStatementLevel.Statements.Count(), "Scope reset, should always be two extra statements here!");
              Assert.AreEqual(curVars + 2, deepestDeclarLevel.DeclaredVariables.Count(), "Scope reset should have also reset where the variable was pointing");
          }
  
          [TestMethod]
          public void TestNameCombo()
          {
              GeneratedCode gc = new GeneratedCode();
              var obj = new ROOTNET.NTH1F("hi", "there", 10, 0.0, 10.0);
              var n1 = gc.QueueForTransfer(obj);
              var n2 = gc.QueueForTransfer(obj);
  
              Assert.AreEqual(n1, n2, "Names should be identical");
              Assert.AreEqual(1, gc.VariablesToTransfer.Count(), "# of variables to move over wire");
          }
  
          [TestMethod]
          [ExpectedException(typeof(InvalidOperationException))]
          public void TestAddOneLevelUpTopLevel()
          {
              GeneratedCode gc = new GeneratedCode();
              gc.AddOneLevelUp(DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)));
          }
  
          [TestMethod]
          public void TestAddOneLevelUpSingleLevel()
          {
              GeneratedCode gc = new GeneratedCode();
              gc.Add(new Statements.StatementInlineBlock());
              gc.AddOneLevelUp(DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)));
              Assert.AreEqual(1, gc.CodeBody.DeclaredVariables.Count(), "Expected top level declaration");
          }
  
          [TestMethod]
          [ExpectedException(typeof(InvalidOperationException))]
          public void TestAddOutsideLoopWithNothing()
          {
              var target = new GeneratedCode();
              target.AddOutsideLoop(DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)));
          }
  
          [TestMethod]
          [ExpectedException(typeof(InvalidOperationException))]
          public void TestAddOutsideLoopWithJustInlineBLock()
          {
              var target = new GeneratedCode();
              target.Add(new Statements.StatementInlineBlock());
              target.AddOutsideLoop(DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)));
          }
  
          [TestMethod]
          public void TestAddOutsideLoopWithJustInLoop()
          {
              var target = new GeneratedCode();
              var blk = new Statements.StatementInlineBlock();
              target.Add(blk);
              target.Add(new LINQToTTreeLib.Tests.TestUtils.SimpleLoop());
              target.Add(new Statements.StatementInlineBlock());
              target.AddOutsideLoop(DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)));
              Assert.AreEqual(1, blk.DeclaredVariables.Count(), "# of loop declared variables");
          }
  
          [TestMethod]
          public void TestAddOutsideLoopWithJustASingleLoop()
          {
              var target = new GeneratedCode();
              var blk = new Statements.StatementInlineBlock();
              target.Add(blk);
              target.Add(new LINQToTTreeLib.Tests.TestUtils.SimpleLoop());
              target.AddOutsideLoop(DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)));
              Assert.AreEqual(1, blk.DeclaredVariables.Count(), "# of loop declared variables");
          }
  
          [TestMethod]
          public void TestAddOutsideLoopWithOnStatement()
          {
              var target = new GeneratedCode();
  
gt:             var blk = new Statements.StatementInlineBlock();