using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Ringtoets.Piping.Data;
using Ringtoets.Piping.IO.Calculation;
using Ringtoets.Piping.IO.Properties;
namespace Ringtoets.Piping.IO.Builders
{
///
/// This class represents objects which were imported from a DSoilModel database. Instances of this class are transient and are not to be used
/// once the DSoilModel database has been imported.
///
internal class SoilLayer2D
{
///
/// Creates a new instance of .
///
public SoilLayer2D()
{
InnerLoops = new Collection>();
}
///
/// Gets the outer loop of the as a of .
///
internal HashSet OuterLoop { get; set; }
///
/// Gets the of inner loops (as of ) of the .
///
internal Collection> InnerLoops { get; private set; }
///
/// Constructs a (1D) based on the and set for the .
///
/// The point from which to take a 1D profile.
/// The bottom level of the .
///
internal IEnumerable AsPipingSoilLayers(double atX, out double bottom)
{
bottom = Double.MaxValue;
var result = new Collection();
if (OuterLoop != null)
{
Collection outerLoopIntersectionHeights = GetLoopIntersectionHeights(OuterLoop, atX);
if (outerLoopIntersectionHeights.Count > 0)
{
IEnumerable> innerLoopsIntersectionHeights = InnerLoops.Select(loop => GetLoopIntersectionHeights(loop, atX));
IEnumerable> innerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(innerLoopsIntersectionHeights).ToList();
IEnumerable> outerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(outerLoopIntersectionHeights).ToList();
var currentBottom = outerLoopIntersectionHeightPairs.First().Item1;
var heights = new List();
heights.AddRange(innerLoopIntersectionHeightPairs.Where(p => p.Item1 >= currentBottom).Select(p => p.Item1));
heights.AddRange(outerLoopIntersectionHeightPairs.Select(p => p.Item2));
foreach (var height in heights.Where(height => !innerLoopIntersectionHeightPairs.Any(tuple => HeightInInnerLoop(tuple, height))))
{
result.Add(new PipingSoilLayer(height));
}
bottom = EnsureBottomOutsideInnerLoop(innerLoopIntersectionHeightPairs, currentBottom);
}
}
return result;
}
private double EnsureBottomOutsideInnerLoop(IEnumerable> innerLoopIntersectionHeightPairs, double bottom)
{
var newBottom = bottom;
var heigthPairArray = innerLoopIntersectionHeightPairs.ToList();
var overlappingInnerLoop = heigthPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom));
while (overlappingInnerLoop != null)
{
newBottom = overlappingInnerLoop.Item2;
overlappingInnerLoop = heigthPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom));
}
return newBottom;
}
private bool HeightInInnerLoop(Tuple tuple, double height)
{
return height <= tuple.Item2 && height > tuple.Item1;
}
private bool BottomInInnerLoop(Tuple tuple, double height)
{
return height < tuple.Item2 && height >= tuple.Item1;
}
private IEnumerable> GetOrderedStartAndEndPairsIn1D(IEnumerable> innerLoopsIntersectionPoints)
{
Collection> result = new Collection>();
foreach (var innerLoopIntersectionPoints in innerLoopsIntersectionPoints)
{
foreach (var tuple in GetOrderedStartAndEndPairsIn1D(innerLoopIntersectionPoints))
{
result.Add(tuple);
}
}
return result;
}
private static Collection> GetOrderedStartAndEndPairsIn1D(IEnumerable innerLoopIntersectionPoints)
{
var result = new Collection>();
var orderedHeights = innerLoopIntersectionPoints.OrderBy(v => v).ToList();
for (int i = 0; i < orderedHeights.Count; i = i+2)
{
var first = orderedHeights[i];
var second = orderedHeights[i+1];
result.Add(new Tuple(first, second));
}
return result;
}
private Collection GetLoopIntersectionHeights(HashSet loop, double atX)
{
Collection intersectionPointY = new Collection(); ;
for (int segmentIndex = 0; segmentIndex < loop.Count; segmentIndex++)
{
var intersectionPoint = GetSegmentIntersectionAtX(loop, segmentIndex, atX);
if (intersectionPoint.Length > 0)
{
intersectionPointY.Add(intersectionPoint[1]);
}
else if (IsVerticalAtX(GetSegmentWithStartAtIndex(loop, segmentIndex), atX))
{
throw new SoilLayer2DConversionException(String.Format(Resources.Error_CanNotDetermine1DProfileWithVerticalSegmentsAtX, atX));
}
}
return intersectionPointY;
}
private bool IsVerticalAtX(Point3D[] segment, double atX)
{
return Math.Abs(segment[0].X - atX) + Math.Abs(segment[1].X - atX) < Math2D.EpsilonForComparisons;
}
private static Point3D[] GetSegmentWithStartAtIndex(HashSet loop, int i)
{
var current = loop.ElementAt(i);
var next = loop.ElementAt((i + 1) % loop.Count);
return new[]
{
current,
next
};
}
private static double[] GetSegmentIntersectionAtX(HashSet loop, int segmentIndex, double atX)
{
Point3D[] segment = GetSegmentWithStartAtIndex(loop, segmentIndex);
return Math2D.LineSegmentIntersectionWithLine(new[]
{
segment[0].X,
segment[1].X,
atX,
atX
}, new[]
{
segment[0].Z,
segment[1].Z,
0,
1
});
}
}
}