//<Get Shell Geometry Poly Loop RHR>
        //Written Jan 31, 2013 by Chien Si Harriman, Senior Product Manager, Carmel Software Corporation
        //Designed to take a Dictionary <key=spaceId/SurfNum, value = List of cartesian coordinates that have been pulled 
        //from a gbXML geometry format file.
        //This list of cart coordinates are associated with a key whoese string name = spaceId+surface number
        //This key was generated in the function GetShellGeomPts
        //------------------</p>
        //Each set of points are then turned into vectors, which are then put through a cross product to determine the 
        //normal vector.  we only arbitrarily take the first three points in the list, which potentially could cause some issue.
        //This is planned to be fixed in a future release.
        //The normal vector calculated is the value in the key value pair, the key being the spaceId+surfaceNumber,
        //The Dictionary is returned with it includes a key value pair for each surface it has analyzed.
        //Therefore, if the Dictionary sent to it has 12 key value pairs, then it will return 12 key value pairs as well.
        //This is not checked for explicitly but mentioned for clarity.
        public static Dictionary<string, List<VectorMath.Vector.CartVect>> GetShellGeomPolyRHR(Dictionary<string, List<VectorMath.Vector.MemorySafe_CartCoord>> PtsList)
        {
            //reg expressions
            string iDPatt = "(.+)[^/][0-9]";
            string numPatt = "[0-9]+";

            //initialize variables needed in this method
            VectorMath.Vector.CartVect unitRHR = new VectorMath.Vector.CartVect();
            List<Vector.MemorySafe_CartCoord> vCoords = new List<Vector.MemorySafe_CartCoord>();
            List<Vector.CartVect> vVect = new List<Vector.CartVect>();
            string spaceId = "none";
            string spacenum = "";
            //dictionary that will be returned by this method
            Dictionary<string, List<VectorMath.Vector.CartVect>> plRHRDict = new Dictionary<string, List<VectorMath.Vector.CartVect>>();

            //begin iterating through the Cartesian Points passed into the method (PtsList)
            for (int i = 0; i < PtsList.Count; i++)
            {
                //get the identification strings associated with each list of points in the dictionary passed to the method
                string spaceSurf = PtsList.Keys.ElementAt(i);
                foreach (Match match in Regex.Matches(spaceSurf, iDPatt))
                {
                    spaceId = match.ToString();
                }
                string spaceSurf2 = PtsList.Keys.ElementAt(i);
                foreach (Match match in Regex.Matches(spaceSurf2, numPatt))
                {
                    spacenum = match.ToString();
                }

                //take the list of coordinates and store them locally 
                //this step does not need to be taken, but it does simplify the coding a little bit.
                foreach (Vector.MemorySafe_CartCoord coord in PtsList.Values.ElementAt(i))
                {
                    vCoords.Add(coord);
                }
                //just arbitrarily take the first 3 coordinates
                //this can lead to bad results, but is used until the next release of the software
                VectorMath.Vector.CartVect v1 = VectorMath.Vector.CreateVector(vCoords[0], vCoords[1]);
                VectorMath.Vector.CartVect v2 = VectorMath.Vector.CreateVector(vCoords[1], vCoords[2]);
                unitRHR = VectorMath.Vector.CrossProduct(v1, v2);
                unitRHR = Vector.UnitVector(unitRHR);
                vVect.Add(unitRHR);
                vCoords.Clear();

            }
            plRHRDict.Add(spaceId, vVect);
            return plRHRDict;
        }
        public static bool TestBuildingStoryRHR(List<XmlDocument> gbXMLDocs, List<XmlNamespaceManager> gbXMLnsm, ref CampusReport cr)
        {
            

            
            //stores the list of z heights for both files
            List<List<string>> fileLevelZz = new List<List<string>>();
            //stores the RHR x product and the corresonding z height for a level
            Dictionary<string, VectorMath.Vector.CartVect> levelVct = new Dictionary<string, VectorMath.Vector.CartVect>();
            

            VectorMath.Vector.CartVect vector = new VectorMath.Vector.CartVect();

            int errorCount = 0;
            for (int i = 0; i < gbXMLDocs.Count; i++)
            {
                try
                {
                    //refresh
                    //stores the level's z heights
                    List<string> LevelZs = new List<string>();
                    //stores a list of the RHR x product and corresponding z height for both files
                    List<Dictionary<string, VectorMath.Vector.CartVect>> fileLevelVct = new List<Dictionary<string, VectorMath.Vector.CartVect>>();

                    XmlDocument gbXMLTestFile = gbXMLDocs[i];
                    XmlNamespaceManager gbXMLns = gbXMLnsm[i];
                    //maybe it would be good if the test result spits out the name of the story?  TBD
                    XmlNodeList PlanarGeometry = gbXMLTestFile.SelectNodes("/gbXMLv5:gbXML/gbXMLv5:Campus/gbXMLv5:Building/gbXMLv5:BuildingStorey/gbXMLv5:PlanarGeometry", gbXMLns);
                    XmlNodeList PolyLoops = gbXMLTestFile.SelectNodes("/gbXMLv5:gbXML/gbXMLv5:Campus/gbXMLv5:Building/gbXMLv5:BuildingStorey/gbXMLv5:PlanarGeometry/gbXMLv5:PolyLoop", gbXMLns);
                    int nodecount = PolyLoops.Count;
                    //get the normals for each level in the Standard File
                    //get the z-coordinate for each level (we assume that the levels are going to be parallel to Z
                    LevelZs = GetLevelZs(PlanarGeometry, LevelZs);
                    if (LevelZs.Count == 0)
                    {
                        logger.Info("PROGRAMMER's NOTE: No level polyloops found in this file.  Level polyloop checks are being ignored.");
                        return false;
                    }
                    foreach (string level in LevelZs)
                    {
                        //a simple attempt to filter out exceptions, which could be returned in some instances
                        if (level.Length < 10)
                        {
                            vector = GetPolyLoopXProduct(PlanarGeometry, level);
                            string levelValue = level;
                            //if (i == 0) { levelValue += "-T"; }
                            //else { levelValue += "-S"; }
                            levelVct.Add(levelValue, vector);
                        }
                    }
                    fileLevelZz.Add(LevelZs);
                    fileLevelVct.Add(levelVct);

                    //reporting 
                    if (i % 2 != 0)
                    {
                        if(fileLevelVct[0].Count > 0)
                        {
                            Dictionary<string, VectorMath.Vector.CartVect> standDict = fileLevelVct[1];
                            Dictionary<string, VectorMath.Vector.CartVect> testDict = fileLevelVct[0];
                            foreach (KeyValuePair<string, VectorMath.Vector.CartVect> pair in standDict)
                            {

                                if (testDict.ContainsKey(pair.Key))
                                {
                                    logger.Info("PROGRAMMERS NOTE: While searching for matching building levels, there has been a Successful match.  Building Story Level " + pair.Key + " in the Standard file found a match in the Test File.");
                                    //perform cross product again of the two vectors in question.  The result should be a zero since they should be parallel
                                    VectorMath.Vector.CartVect rhrTestVector = VectorMath.Vector.CrossProduct(testDict[pair.Key], standDict[pair.Key]);
                                    if (Math.Abs(rhrTestVector.X) <= 0.1 && Math.Abs(rhrTestVector.Y) == 0.1 && Math.Abs(rhrTestVector.Z) == 0.1)
                                    {
                                        logger.Info("TEST FILE SUCCESS: For this level match, there is Normal Vector Test Success.  The right hand rule test identified a parallel normal vector for Level " + pair.Key + " in both the Standard and Test gbXML Files.");
                                    }
                                    else
                                    {
                                        logger.Info("TEST FILE FAILURE: For this level match, there not Normal Vector Test Success.  The right hand rule test shows the vectors are not parallel for Level " + pair.Key + " in both the Standard and Test gbXML Files.");
                                    }

                                }
                                else
                                {
                                    logger.Fatal("The right hand rule test for Level " + pair.Key + " in the Standard File could not be completed.  A match for this level could not be found in the test file.");
                                    return false;
                                }
                            }
                        }

                    }
                    else { continue; }

                    //need to comapre and have if then statement depending on the outcome of the accuracy tests
                    if (errorCount == 0)
                    {
                        logger.Info("TEST FILE SUCCESS:  Building Stories RHR in the Test File match the RHR in the Standard File for all Levels.");
                    }
                    else
                    {
                        logger.Info("TEST FILE FAILURE: Not all levels in the Standard File found equivalent levels and normal vectors in the Test File.");
                        return false;
                    }
                    
                }
                catch (Exception e)
                {
                    logger.Debug(e.ToString());
                    logger.Fatal(" Failed to complete RHR Test for the Building Storey Nodes.  Exception noted.");
                    return false;
                }
            }
            return true;
        }
        //this is a simple way to get the polyLoop X product.
        //this is a support function used by the Function TestBuildingStory RHR above
        //This X Product routine is the first attempt to produce a X product from coordinates  Since the coordinates used to define
        //a level plane never create an irregular polygon, this scheme worked.  
        //it will only assuredly work properly for a triangle, square, or rectangle.  Shapes other than these should use subsequent XProduct
        //functions as created below.
        //Created by CHarriman, Senior Product Manager Carmel Software
        //Nov 2012
        public static VectorMath.Vector.CartVect GetPolyLoopXProduct(XmlNodeList PlanarGeometry, string level)
        {
            int cartPtCount = 0;
            VectorMath.Vector.CartVect xProd = new VectorMath.Vector.CartVect();
            //gathers all the cartesian points in a given polyloop
            int nodecount = PlanarGeometry.Count;
            VectorMath.Vector.CartCoord[] vCoords = new VectorMath.Vector.CartCoord[3];
            foreach (XmlNode PolyLoops in PlanarGeometry)
            {
                foreach (XmlNode cartesianPoints in PolyLoops)
                {

                    //test the polyloop RHR convention
                    //count the total number of cartesian coordinates
                    int coordcount = cartesianPoints.ChildNodes.Count;
                    //I may want to test the number of coordinates to make sure it matches, or if it has a minimum number of coords
                    if (coordcount < minPlanePoints)
                    {
                        //result += "Insufficient number of cartesian points to define a plane";
                        return xProd;
                    }
                    else
                    {
                        cartPtCount = 0;
                        //gets a set of XYZ coordinates, one at a time
                        foreach (XmlNode coordinates in cartesianPoints.ChildNodes)
                        {
                            if (cartPtCount < 3)
                            {
                                VectorMath.Vector.CartCoord vC = new VectorMath.Vector.CartCoord();
                                vCoords[cartPtCount] = vC;
                            }
                            else { break; }

                            int crdCount = 1;
                            //gets each coordinate one at a time
                            //filtering through the inner children of the PolyLoop
                            foreach (XmlNode coordinate in coordinates.ChildNodes)
                            {
                                double coord = Convert.ToDouble(coordinate.InnerText);
                                switch (crdCount)
                                {
                                    case 1:
                                        vCoords[cartPtCount].X = coord;
                                        break;
                                    case 2:
                                        vCoords[cartPtCount].Y = coord;
                                        break;
                                    case 3:
                                        vCoords[cartPtCount].Z = coord;
                                        break;
                                    default:
                                        break;
                                }
                                if (vCoords[cartPtCount].Z.ToString() == level) { break; };
                                crdCount++;
                            }

                            cartPtCount++;
                        }

                    }
                }
                if (vCoords[(cartPtCount - 1)].Z.ToString() == level) { break; }
            }
            //Get the Cross Product
            VectorMath.Vector.CartVect v1 = VectorMath.Vector.CreateVector(vCoords[0], vCoords[1]);
            VectorMath.Vector.CartVect v2 = VectorMath.Vector.CreateVector(vCoords[1], vCoords[2]);
            xProd = VectorMath.Vector.CrossProduct(v1, v2);
            xProd = Vector.UnitVector(xProd);
            return xProd;

        }
        public static DOEgbXMLReportingObj TestBuildingStoryRHR(List<XmlDocument> gbXMLDocs, List<XmlNamespaceManager> gbXMLnsm, DOEgbXMLReportingObj report, string Units)
        {
            //this summary is text that describes to a lay user what this test does, and how it works functionally.  The user should have some familiarity with the basic knowledge of gbXML 
            //added Feb 13 2013
            report.testSummary = "This test analyzes each of the story PolyLoop coordinates in the standard and test files.  These PolyLoop ";
            report.testSummary += "coordinates define the z-height and orientation of each story plane.  This test analyzes the normal vector ";
            report.testSummary += "created by the PolyLoop coordinates.  The PolyLoop coordinates must be sequenced in a counterclockwise manner ";
            report.testSummary += " such that when the right hand rule is applied to this sequence of coordinates, a resultant normal vector ";
            report.testSummary += " will point in the +z direction.";
            report.testSummary += "  If the PolyLoop coordinates do not form vectors that point in the +Z direction";
            report.testSummary += " (when the right hand rule is applied), then this test will fail.  It is assumed that the vectors that define";
            report.testSummary += " the story plane will be parallel to the X-Y axis.The tolerance is always zero for this test, ";
            report.testSummary += "meaning the resulting unit vector will point in the positive Z direction with no margin for error.";

            report.unit = Units;

            //stores the level's z heights
            List<string> LevelZs = new List<string>();
            //stores the list of z heights for both files
            List<List<string>> fileLevelZz = new List<List<string>>();
            //stores the RHR x product and the corresonding z height for a level
            Dictionary<string, VectorMath.Vector.CartVect> levelVct = new Dictionary<string, VectorMath.Vector.CartVect>();
            //stores a list of the RHR x product and corresponding z height for both files
            List<Dictionary<string, VectorMath.Vector.CartVect>> fileLevelVct = new List<Dictionary<string, VectorMath.Vector.CartVect>>();

            VectorMath.Vector.CartVect vector = new VectorMath.Vector.CartVect();

            int errorCount = 0;
            for (int i = 0; i < gbXMLDocs.Count; i++)
            {


                try
                {
                    //refresh
                    LevelZs.Clear();
                    levelVct.Clear();

                    XmlDocument gbXMLTestFile = gbXMLDocs[i];
                    XmlNamespaceManager gbXMLns = gbXMLnsm[i];
                    //maybe it would be good if the test result spits out the name of the story?  TBD
                    XmlNodeList PlanarGeometry = gbXMLTestFile.SelectNodes("/gbXMLv5:gbXML/gbXMLv5:Campus/gbXMLv5:Building/gbXMLv5:BuildingStorey/gbXMLv5:PlanarGeometry", gbXMLns);
                    XmlNodeList PolyLoops = gbXMLTestFile.SelectNodes("/gbXMLv5:gbXML/gbXMLv5:Campus/gbXMLv5:Building/gbXMLv5:BuildingStorey/gbXMLv5:PlanarGeometry/gbXMLv5:PolyLoop", gbXMLns);
                    int nodecount = PolyLoops.Count;
                    //get the normals for each level in the Standard File
                    //get the z-coordinate for each level (we assume that the levels are going to be parallel to Z
                    LevelZs = GetLevelZs(PlanarGeometry, LevelZs);
                    foreach (string level in LevelZs)
                    {
                        //a simple attempt to filter out exceptions, which could be returned in some instances
                        if (level.Length < 10)
                        {
                            vector = GetPolyLoopXProduct(PlanarGeometry, level);
                            string levelValue = level;
                            //if (i == 0) { levelValue += "-T"; }
                            //else { levelValue += "-S"; }
                            levelVct.Add(levelValue, vector);
                        }
                    }
                    fileLevelZz.Add(LevelZs);
                    fileLevelVct.Add(levelVct);

                    //reporting 
                    if (i % 2 != 0)
                    {
                        Dictionary<string, VectorMath.Vector.CartVect> standDict = fileLevelVct[1];
                        Dictionary<string, VectorMath.Vector.CartVect> testDict = fileLevelVct[0];
                        foreach (KeyValuePair<string, VectorMath.Vector.CartVect> pair in standDict)
                        {

                            if (testDict.ContainsKey(pair.Key))
                            {
                                report.MessageList.Add("While searching for matching building levels, there has been a Successful match.  Building Story Level " + pair.Key + " in the Standard file found a match in the Test File.");
                                report.passOrFail = true;
                                //perform cross product again of the two vectors in question.  The result should be a zero since they should be parallel
                                VectorMath.Vector.CartVect rhrTestVector = VectorMath.Vector.CrossProduct(testDict[pair.Key], standDict[pair.Key]);
                                if (rhrTestVector.X == 0 && rhrTestVector.Y == 0 && rhrTestVector.Z == 0)
                                {
                                    report.MessageList.Add("For this level match, there is Normal Vector Test Success.  The right hand rule test identified a parallel normal vector for Level " + pair.Key + " in both the Standard and Test gbXML Files.");
                                    report.passOrFail = true;
                                }
                                else
                                {
                                    VectorMath.Vector.CartVect rhrTestVectorU = VectorMath.Vector.UnitVector(rhrTestVector);
                                    //create a test to determine the angular difference between the two vectors is within tolerance
                                    //|A||B|cos theta = A x B

                                    //if the angle is within the allowable tolerance, then pass the test with a note that the vectors
                                    //were not parallel
                                }

                            }
                            else
                            {
                                report.MessageList.Add("The right hand rule test for Level " + pair.Key + " in the Standard File could not be completed.  A match for this level could not be found in the test file.");
                                report.passOrFail = false;
                                errorCount++;
                            }
                        }

                    }
                    else { continue; }

                    //need to comapre and have if then statement depending on the outcome of the accuracy tests
                    if (errorCount == 0)
                    {
                        report.longMsg = "Test Success:  Building Stories RHR in the Test File match the RHR in the Standard File for all Levels.";
                    }
                    else
                    {
                        report.longMsg = "Not all levels in the Standard File found equivalent levels and normal vectors in the Test File.";
                    }
                    return report;
                }
                catch (Exception e)
                {
                    report.MessageList.Add(e.ToString());
                    report.longMsg = " Failed to complete RHR Test for the Building Storey Nodes.  Exception noted.";
                    report.passOrFail = false;
                    return report;
                }
            }
            report.longMsg = "Fatal " + report.testType + " Test Failure";
            return report;
        }