text:
namespace R3.Geometry
{
	using R3.Core;
	using R3.Math;
	using System.Collections.Generic;
	using System.IO;
	using System.Linq;
	using Math = System.Math;

	/// <summary>
	/// A class to play around with H3 honecombs
	/// </summary>
	public class H3
	{
		/// <summary>
		/// We can track a cell by its ideal vertices.
		/// We'll work with these in the plane.
		/// </summary>
		public class Cell
		{
			public Cell( Facet[] facets ) : this( -1, facets )
			{
			}

			public Cell( int p, Facet[] facets )
			{
				P = p;
				Facets = facets;
				Depths = new int[4];
			}

			public int P; // Number of edges in polygon
			public Facet[] Facets;
			public Vector3D Center;

			// Not necessary.
			public Mesh Mesh;

			/// <summary>
			/// Used to track recursing depth of reflections across various mirrors.
			/// </summary>
			public int[] Depths;

			public bool IdealVerts
			{
				get
				{
					if( this.Verts.Count() == 0 )
						return false;
					return Tolerance.Equal( this.Verts.First().MagSquared(), 1 );
				}
			}

			public void AppendAllEdges( HashSet<Edge> edges )
			{
				foreach( Facet f in Facets )
					f.AppendAllEdges( edges );
			}

			/// <summary>
			/// In Ball model.
			/// </summary>
			public void CalcCenterFromFacets()
			{
				Vector3D center = new Vector3D();
				foreach( Sphere s in this.Facets.Select( f => f.Sphere ) )
				{
					if( s.IsPlane )
						continue;

					Vector3D sCenter = s.Center;
					double abs = s.Center.Abs();
					sCenter.Normalize();
					sCenter *= abs - s.Radius;
					center += sCenter;
				}

				center /= this.Facets.Length;
				this.Center = center;
			}

			public class Facet
			{
				public Facet( Vector3D[] verts ) { Verts = verts; }
				public Facet( Sphere sphere ) { Sphere = sphere; }

				/// <summary>
				/// The facet vertices.
				/// May live on the plane, in the ball model, etc. as needed.
				/// </summary>
				public Vector3D[] Verts;

				/// <summary>
				/// This is an alternate way to track facets, and *required*
				/// for Lorentzian honeycombs, because all the vertices are hyperideal.
				/// It is expected that these are always in the Ball model.
				/// </summary>
				public Sphere Sphere { get; set; }
				private Vector3D CenterInBall
				{
					get
					{
						if( Infinity.IsInfinite( Sphere.Radius ) )
							return new Vector3D();

						// Calcs based on orthogonal circles.
						// http://mathworld.wolfram.com/OrthogonalCircles.html
						double d = Math.Sqrt( 1 + Sphere.Radius * Sphere.Radius );
						Vector3D center = Sphere.Center;
						center.Normalize();
						center *= ( d - Sphere.Radius );
						return center;
					}
				}

				public void CalcSphereFromVerts( Geometry g )
				{
					switch( g )
					{
						case Geometry.Spherical:

							Sphere = new Sphere();
							if( Verts.Where( v => v == new Vector3D() ).Count() > 0 )	// XXX - not general, I'm so lazy.
							{
								Vector3D[] nonZero = Verts.Where( v => v != new Vector3D() ).ToArray();
								Sphere.Radius = double.PositiveInfinity;
								Sphere.Center = nonZero[0].Cross( nonZero[1] );
							}
							else
							{
								// The sphere intersects the unit-sphere at a unit-circle (orthogonal to the facet center direction).
								Vector3D direction = new Vector3D();
								foreach( Vector3D v in Verts )
									direction += v;
								direction /= Verts.Length;
								direction.Normalize();

								Vector3D p1 = Euclidean3D.ProjectOntoPlane( direction, new Vector3D(), Verts[0] );
								p1.Normalize();

								Circle3D c = new Circle3D( p1, Verts[0], -p1 );
								Sphere.Center = c.Center;
								Sphere.Radius = c.Radius;
							}

							break;

						case Geometry.Euclidean:

							Sphere = new Sphere();
							Sphere.Radius = double.PositiveInfinity;
							Vector3D v1 = Verts[0], v2 = Verts[1], v3 = Verts[2];
							Sphere.Center = ( v2 - v1 ).Cross( v3 - v1 );
							Sphere.Offset = Euclidean3D.ProjectOntoPlane( Sphere.Center, v1, new Vector3D() );
							break;

						case Geometry.Hyperbolic:
							Sphere = H3Models.Ball.OrthogonalSphereInterior( Verts[0], Verts[1], Verts[2] );
							break;
					}
				}

				public Facet Clone()
				{
					Facet newFacet = new Facet( Verts == null ? null : (Vector3D[])Verts.Clone() );
					newFacet.Sphere = Sphere == null ? null : Sphere.Clone();
					return newFacet;
				}

				public void Reflect( Sphere sphere )
				{
					if( Verts != null )
					{
						for( int i=0; i<Verts.Length; i++ )
							Verts[i] = sphere.ReflectPoint( Verts[i] );
					}

					if( Sphere != null )
						Sphere.Reflect( sphere );
				}

				public Vector3D ID
				{
					get
					{
						Vector3D result = new Vector3D();
						if( Verts != null )
						{
							foreach( Vector3D v in Verts )
								result += v;
							return result;
						}

						if( Sphere != null )
							return Sphere.ID;

						throw new System.ArgumentException();
					}
				}

				public void AppendAllEdges( HashSet<Edge> edges )
				{
					// We can only do this if we have vertices.
					if( Verts == null )
						return;

					for( int i=0; i<Verts.Length; i++ )
					{
						int idx1 = i;
						int idx2 = i == Verts.Length - 1 ? 0 : i + 1;
						edges.Add( new Edge( Verts[idx1], Verts[idx2] ) );
					}
				}
			}

			public class Edge
			{
				public Edge( Vector3D v1, Vector3D v2, bool order = true )
				{
					// Keep things "ordered", so we can easily compare edges.
					if( order )
					{
						Vector3D[] orderedVerts = new Vector3D[] { v1, v2 };
						orderedVerts = orderedVerts.OrderBy( v => v, new Vector3DComparer() ).ToArray();
						Start = orderedVerts[0];
						End = orderedVerts[1];
					}
					else
					{
						Start = v1;
						End = v2;
					}

					Depths = new int[4];
				}

				public Vector3D Start;
				public Vector3D End;

				// The reason we use a vector here is so the components
				// can be interpreted in different color schemes (HLS, RGB, etc.)
				public Vector3D Color;

				/// <summary>
				/// Used to track recursing depth of reflections across various mirrors.
				/// </summary>
				public int[] Depths;

				public Edge Clone()
				{
					return (Edge)MemberwiseClone();
				}

				public Vector3D ID
				{
					get
					{
						return Start + End;
					}
				}

				public Vector3D Opp( Vector3D v )
				{
					return v == Start ? End : Start;
				}

				public void Write( StreamWriter sw, int level )
				{
					sw.WriteLine( string.Format( "{0},{1},{2}", level, Start.ToStringXYZOnly(), End.ToStringXYZOnly() ) );
				}

				public void CopyDepthsFrom( Edge e )
				{
					Depths = (int[])e.Depths.Clone();
				}
			}

			public class EdgeEqualityComparer : IEqualityComparer<Edge>
			{
				public bool Equals( Edge e1, Edge e2 )
				{
					return
						e1.Start.Compare( e2.Start, m_tolerance ) &&
						e1.End.Compare( e2.End, m_tolerance );
				}

				public int GetHashCode( Edge e )
				{
					return e.Start.GetHashCode() ^ e.End.GetHashCode();
				}

				private double m_tolerance = 0.0001;
			}

			public bool HasVerts
			{
				get
				{
					foreach( Facet f in Facets )
						if( f.Verts == null )
							return false;
					return true;
				}
			}

			public IEnumerable<Vector3D> Verts
			{
				get
				{
					foreach( Facet facet in Facets )
					{
						if( facet.Verts != null )
							foreach( Vector3D v in facet.Verts )
								yield return v;
					}
				}
			}

			public Vector3D ID
			{
				get
				{
					Vector3D result = new Vector3D();
					//foreach( Vector3D v in Verts )
						//result += Sterographic.PlaneToSphereSafe( v );	// XXX - what about when not working in plane.

					if( HasVerts )
					{
						foreach( Vector3D v in Verts )
							result += v;
					}
					else
					{
						// Intentionally just use the center.
					}
					result += Center;
					return result;
				}
			}

			public Cell Clone()
			{
				List<Facet> newFacets = new List<Facet>();
				foreach( Facet facet in Facets )
					newFacets.Add( facet.Clone() );

				Cell clone = new Cell( P, newFacets.ToArray() );
				clone.Center = Center;
				clone.Depths = (int[])Depths.Clone();

				if( Mesh != null )
					clone.Mesh = Mesh.Clone();

				return clone;
			}

			/// <summary>
			/// Moves our vertices from the plane to the sphere.
			/// </summary>
			public void ToSphere()
			{
				foreach( Facet facet in Facets )
					for( int i=0; i<P; i++ )
						facet.Verts[i] = Sterographic.PlaneToSphereSafe( facet.Verts[i] );
			}

			/// <summary>
			/// Scales cell to circumsphere.
			/// NOTE: We should already be on a sphere, not the plane.
			/// </summary>
			public void ScaleToCircumSphere( double r )
			{
				foreach( Facet facet in Facets )
					for( int i=0; i<P; i++ )
					{
						Vector3D axis = facet.Verts[i];
						axis.Normalize();
						facet.Verts[i] = axis * r;
					}
			}

			/// <summary>
			/// Apply a Mobius transformation to us (meaning of Mobius transform is on boundary of UHS model).
			/// </summary>
			public void ApplyMobius( Mobius m )
			{
				foreach( Facet facet in Facets )
					for( int i = 0; i < P; i++ )
						facet.Verts[i] = H3Models.Ball.ApplyMobius( m, facet.Verts[i] );
				Center = H3Models.Ball.ApplyMobius( m, Center );
			}

			public void Reflect( Sphere sphere )
			{
				foreach( Facet facet in Facets )
					facet.Reflect( sphere );
				Center = sphere.ReflectPoint( Center );

				if( this.Mesh != null )
				{
					for( int i=0; i<Mesh.Triangles.Count; i++ )
					{
						Mesh.Triangle tri = Mesh.Triangles[i];
						tri.a = sphere.ReflectPoint( tri.a );
						tri.b = sphere.ReflectPoint( tri.b );
						tri.c = sphere.ReflectPoint( tri.c );
						Mesh.Triangles[i] = tri;
					}
				}
			}
		}

		public static class Util
		{
			public static void AddEdges( Cell cell, int level, Dictionary<Cell.Edge,int> completedEdges )
			{
				foreach( Cell.Facet facet in cell.Facets )
				{
					for( int i=0; i<cell.P; i++ )
					{
						int i1 = i;
						int i2 = i == cell.P - 1 ? 0 : i + 1;

						Cell.Edge edge = new Cell.Edge( facet.Verts[i1], facet.Verts[i2] );
						if( completedEdges.ContainsKey( edge ) )
							continue;

						completedEdges.Add( edge, level );
					}
				}
			}

			public static void AddToMeshInternal( Shapeways mesh, Vector3D _v1, Vector3D _v2 )
			{
				// Move to ball.
				//Vector3D v1 = Sterographic.PlaneToSphereSafe( _v1 );
				//Vector3D v2 = Sterographic.PlaneToSphereSafe( _v2 );
				Vector3D v1 = _v1, v2 = _v2;

				System.Func<Vector3D, double> sizeFunc = SizeFuncConst;
				if( m_settings.Halfspace )
				{
					Vector3D[] points = H3Models.UHS.GeodesicPoints( _v1, _v2 );
					if( !m_settings.ThinEdges )
						sizeFunc = v => H3Models.UHS.SizeFunc( v, m_settings.AngularThickness );
					mesh.AddCurve( points, sizeFunc );
				}
				else
				{
					Vector3D[] points = H3Models.Ball.GeodesicPoints( v1, v2 );
					if( !m_settings.ThinEdges )
						sizeFunc = v => H3Models.Ball.SizeFunc( v, m_settings.AngularThickness );
					mesh.AddCurve( points, sizeFunc );
				}
			}

			/// <summary>
			/// Used to help figure out how thin our wires are going to get.
			/// </summary>
			internal static void LogMinSize( Dictionary<Cell.Edge, int> edges )
			{
				double max = 0;
				foreach( Cell.Edge e in edges.Keys )
				{
					max = Math.Max( max, e.Start.Abs() );
					max = Math.Max( max, e.End.Abs() );
				}

				Vector3D v = new Vector3D( 0, 0, max );
				double radius = H3Models.Ball.SizeFunc( v, m_settings.AngularThickness );
				double thickness = radius * 2 * m_settings.Scale;
				System.Console.WriteLine( string.Format( "location = {0}, thickness = {1}", max, thickness ) );

				if( thickness < 0.87 )
					System.Console.WriteLine( "WARNING!!!!!!! Edge thickness will be too small for shapeways.  You will be REJECTED." );
			}

			private static void AddCoordSpheres( Shapeways mesh )
			{
				mesh.AddSphere( new Vector3D( 1.5, 0, 0 ), 0.1 );
				mesh.AddSphere( new Vector3D( 0, 1.5, 0 ), 0.2 );
				mesh.AddSphere( new Vector3D( 0, 0, 1.5 ), 0.3 );
			}
		}

		public enum Output
		{
			STL,
			POVRay
		}

		public class Settings
		{
			public double Scale = 50;	// 10cm ball by default.

			public bool Halfspace = false;
			public int MaxCells = 150000;

			// Ball Model
			public double Ball_MaxLength = 3;
			//public double Ball_MinLength = 0.075;
			public double Ball_MinLength = 0.15;
			//public double Ball_MinLength = 0.05;
			//private static double Ball_MinLength = 0.45;	// lamp
			public double Ball_Cutoff = 0.95;

			// UHS
			//public double UHS_MinEdgeLength = .09;
			//public double UHS_MaxBounds = 6.5;
			public double UHS_MinEdgeLength = 0.03;
			public double UHS_MaxBounds = 2;
			public double UHS_Horocycle = 0.25;

			// Bananas
			public bool ThinEdges = false;
			public double AngularThickness = 0.06;	// an angle (the slope of the banana)
			//public double AngularThickness = 0.04;
			//public double AngularThickness = 0.25;

			// Position and associated Mobius to apply
			public Polytope.Projection Position = Polytope.Projection.CellCentered;
			public Mobius Mobius = Mobius.Identity();

			public Output Output = Output.POVRay;
		}

		public static Settings m_settings = new Settings();
		//public static string m_baseDir = @"C:\Users\hrn\Documents\roice\povray\H3\";
		public static string m_baseDir = @".\";

		public static void GenHoneycombs()
		{
			//GenHoneycomb( Honeycomb.H434 );

			//GenHoneycomb( Honeycomb.H435 );
			//GenHoneycomb( Honeycomb.H534 );
			//GenHoneycomb( Honeycomb.H535 );
			//GenHoneycomb( Honeycomb.H353 );

			//GenHoneycomb( Honeycomb.H336 );
			//GenHoneycomb( Honeycomb.H436 );
			//GenHoneycomb( Honeycomb.H536 );
			//GenHoneycomb( Honeycomb.H344 );

			//GenHoneycomb( Honeycomb.H636 );
			//GenHoneycomb( Honeycomb.H444 );
			//GenHoneycomb( Honeycomb.H363 );

			GenHoneycomb( EHoneycomb.H33I );

			//H3Fundamental.Generate( Honeycomb.H435, m_settings );
		}

		private static int[] m_levelsToKeep = new int[] { 1, 2, 3, 4, 5 };
		private static double[] m_rangeToKeep = new double[] { 0.9, 0.96 };
		private static bool CheckRange( Vector3D v )
		{
			double abs = v.Abs();
			return
				abs > m_rangeToKeep[0] &&
				abs < m_rangeToKeep[1];
		}

		private static void SetupShapewaysSettings( Settings settings, EHoneycomb honeycomb )
		{
			switch( honeycomb )
			{
				case EHoneycomb.H336:
				case EHoneycomb.H344:
				{
					break;
				}
				case EHoneycomb.H436:
				{
					//settings.Ball_MinLength = 0.08;
					settings.Ball_MinLength = 0.08;
					break;
				}
				case EHoneycomb.H536:
				{
					//settings.Ball_MinLength = 0.05;
					settings.Ball_MinLength = 0.03;
					break;
				}
			}
		}

		private static void SetupSettings( EHoneycomb honeycomb )
		{
			switch( honeycomb )
			{
				case EHoneycomb.H435:
				{
					m_settings.Ball_Cutoff = 0.95;
					//m_settings.AngularThickness = 0.163;

					// Sandstone
					m_settings.Position = Polytope.Projection.VertexCentered;
					m_settings.Ball_Cutoff = 0.88;
					m_settings.AngularThickness = 0.18;

					// Paper
					m_settings.Position = Polytope.Projection.CellCentered;
					m_settings.Ball_Cutoff = 0.96;
					m_settings.AngularThickness = 0.08;

					break;
				}
				case EHoneycomb.H534:
				{
					m_settings.Ball_Cutoff = m_settings.ThinEdges ? 0.984 : 0.95;
					//m_settings.Ball_MaxLength = 1;
					//m_settings.Ball_MinLength = 0.05;

					// Sandstone
					//m_settings.Position = Polytope.Projection.CellCentered;
					//m_settings.Ball_Cutoff = 0.88;
					//m_settings.AngularThickness = 0.18;

					// Laser Crystal
					m_settings.AngularThickness = 0.1;
					m_settings.Ball_Cutoff = 0.98;
					m_settings.Output = Output.STL;

					break;
				}
				case EHoneycomb.H535:
				{
					//m_settings.Ball_Cutoff = m_settings.ThinEdges ? 0.993 : 0.96;
					//m_settings.Postion = Position.VertexCentered;
					m_settings.ThinEdges = true;
					m_settings.Ball_Cutoff = 0.98;
					break;
				}
				case EHoneycomb.H353:
				{
					bool big = true;
					if( big )
					{
						m_settings.Scale = 65;
						m_settings.Ball_Cutoff = 0.96;
						m_settings.AngularThickness = 0.165;
					}
					else
					{
						// Defaults
					}
					break;
				}
				case EHoneycomb.H336:
				{
					/* Settings for the AMS visual insight picture.
					m_settings.Halfspace = true;
					m_settings.UHS_MinEdgeLength = 0.025;
					m_settings.AngularThickness = 0.075;
					 */

					break;
				}
				case EHoneycomb.H337:
				{
					m_settings.Scale = 75;
					//m_settings.AngularThickness = 0.202;
					m_settings.AngularThickness = 0.1;
					break;
				}
				case EHoneycomb.H436:
				{
					break;
				}
				case EHoneycomb.H536:
				{
					m_settings.Ball_MinLength = 0.02;
					break;
				}
				case EHoneycomb.H344:
				{
					m_settings.Ball_MinLength = 0.05;
					break;
				}
				case EHoneycomb.H33I:
				{
					m_settings.AngularThickness = .17;
					break;
				}
			}

			if( m_settings.Output == Output.POVRay )
			{
				// Wiki
				m_settings.Ball_MinLength = 0.0018;	// 534
				m_settings.Ball_MinLength = 0.005;	// 344
				m_settings.Ball_MinLength = 0.003; // others

				m_settings.Ball_MinLength = 0.001; // duals
				//m_settings.MaxCells = 300000;
				//m_settings.MaxCells = 100000;
				m_settings.MaxCells = 500000;
				//m_settings.Position = Polytope.Projection.EdgeCentered;

				// Finite
				//m_settings.Position = Polytope.Projection.CellCentered;
				//m_settings.Ball_Cutoff = 0.997;
				//m_settings.Ball_Cutoff = 0.999;	//535
			}
		}

		/// <summary>
		/// Naming is from Coxeter's table.
		/// Side lengths of fundamental right angled triangle.
		/// We'll use these to calc isometries to change the canonical position.
		/// 2 * phi = edge length
		/// chi = circumradius
		/// psi = inradius
		/// </summary>
		public static void HoneycombData( EHoneycomb honeycomb, out double phi, out double chi, out double psi )
		{
			phi = chi = psi = -1;
			chi = Honeycomb.CircumRadius( honeycomb );
			psi = Honeycomb.InRadius( honeycomb );
			phi = Honeycomb.EdgeLength( honeycomb ) / 2;
		}

		public static void SetupCentering( EHoneycomb honeycomb, Settings settings, double phi, double chi, double psi, ref Polytope.Projection projection )
		{
			settings.Mobius = Mobius.Identity();
			switch( settings.Position )
			{
				case Polytope.Projection.CellCentered:
					//m_settings.Mobius = Mobius.Scale( 5 );	// {6,3,3}, {4,3,3} cell-centered (sort of)
					break;
				case Polytope.Projection.FaceCentered:

					if( psi != -1 )
						settings.Mobius = Mobius.Scale( H3Models.UHS.ToE( psi ) );

					break;
				case Polytope.Projection.EdgeCentered:

					if( psi != -1 )
					{
						settings.Mobius = Mobius.Scale( H3Models.UHS.ToE( Honeycomb.MidRadius( honeycomb ) ) );
						projection = Polytope.Projection.EdgeCentered;
					}
					else
					{
						// This only works for the finite cells.

						// We can get the scaling distance we need from the analogue of the
						// pythagorean theorm for a hyperbolic right triangle.
						// The hypotenuse is the circumradius (chi), and the other side is the 1/2 edge length (phi).
						// http://en.wikipedia.org/wiki/Pythagorean_theorem#Hyperbolic_geometry
						double tempScale = DonHatch.acosh( Math.Cosh( chi ) / Math.Cosh( phi ) );
						settings.Mobius = Mobius.Scale( H3Models.UHS.ToE( tempScale ) );

						projection = Polytope.Projection.VertexCentered;
					}

					break;
				case Polytope.Projection.VertexCentered:

					if( chi != -1 )
					{
						settings.Mobius = Mobius.Scale( H3Models.UHS.ToE( chi ) );
						projection = Polytope.Projection.VertexCentered;
					}

					break;
			}
		}

		public static void GenHoneycomb( EHoneycomb honeycomb )
		{
			if( honeycomb == EHoneycomb.H434 )
			{
				Euclidean.GenEuclidean();
				return;
			}

			if( H3Supp.IsExotic( honeycomb ) )
			{
				H3Supp.GenerateExotic( honeycomb, m_settings );
				return;
			}

			SetupSettings( honeycomb );
			//SetupShapewaysSettings( m_settings, honeycomb );

			Tiling tiling = CellTilingForHoneycomb( honeycomb );

			double cellScale = Honeycomb.CircumRadius( honeycomb );
			if( Infinity.IsInfinite( cellScale ) )
				cellScale = 1;
			else
				cellScale = DonHatch.h2eNorm( cellScale );

			Shapeways mesh = new Shapeways();
			bool finite = cellScale != 1;
			if( finite )
			{
				GenHoneycombInternal( mesh, tiling, honeycomb, cellScale );
			}
			else
			{
				GenHoneycombInternal( mesh, tiling, honeycomb, cellScale );

				mesh = new Shapeways();
				//GenDualHoneycombInternal( mesh, tiling, honeycomb );
			}
		}

		public static Tiling CellTilingForHoneycomb( EHoneycomb honeycomb )
		{
			int p, q, r;
			Honeycomb.PQR( honeycomb, out p, out q, out r );

			// Get data we need to generate the honeycomb.
			Polytope.Projection projection = Polytope.Projection.FaceCentered;
			double phi, chi, psi;
			HoneycombData( honeycomb, out phi, out chi, out psi );

			SetupCentering( honeycomb, m_settings, phi, chi, psi, ref projection );

			Tiling tiling = new Tiling();
			TilingConfig config = new TilingConfig( p, q );
			tiling.GenerateInternal( config, projection );
			return tiling;
		}

		public static Cell.Facet[] GenFacets( Tiling tiling )
		{
			List<Cell.Facet> facets = new List<Cell.Facet>();
			foreach( Tile tile in tiling.Tiles )
			{
				List<Vector3D> points = new List<Vector3D>();
				foreach( Segment seg in tile.Boundary.Segments )
					points.Add( seg.P1 );
				facets.Add( new Cell.Facet( points.ToArray() ) );
			}

			return facets.ToArray();
		}

		public static Cell.Facet[] GenFacetSpheres( Tiling tiling, double inRadius )
		{
			List<Cell.Facet> facets = new List<Cell.Facet>();
			foreach( Tile tile in tiling.Tiles )
			{
				Vector3D facetClosestToOrigin = tile.Boundary.Center;
				facetClosestToOrigin = Sterographic.PlaneToSphereSafe( facetClosestToOrigin );
				facetClosestToOrigin.Normalize();
				facetClosestToOrigin *= DonHatch.h2eNorm( inRadius );
				Sphere facetSphere = H3Models.Ball.OrthogonalSphereInterior( facetClosestToOrigin );
				facets.Add( new Cell.Facet( facetSphere ) );
			}

			return facets.ToArray();
		}

		/// <summary>
		/// Generates H3 honeycombs with cells having a finite number of facets.
		/// </summary>
		private static void GenHoneycombInternal( Shapeways mesh, Tiling tiling, EHoneycomb honeycomb, double cellScale )
		{
			string honeycombString = Honeycomb.String( honeycomb, false );

			int p, q, r;
			Honeycomb.PQR( honeycomb, out p, out q, out r );

			double inRadius = Honeycomb.InRadius( honeycomb );
			Cell.Facet[] facets = GenFacetSpheres( tiling, inRadius );
			Cell first = new Cell( p, facets );

			/*Cell first = new Cell( p, GenFacets( tiling ) );
			first.ToSphere();	// Work in ball model.
			first.ScaleToCircumSphere( cellScale );
			first.ApplyMobius( m_settings.Mobius );*/

			// This is for getting endpoints of cylinders for hollowing.
			bool printVerts = false;
			if( printVerts )
			{
				foreach( Cell.Facet facet in first.Facets )
				{
					Vector3D v = facet.Verts.First();
					v *= m_settings.Scale;
					System.Diagnostics.Trace.WriteLine( string.Format( "Vert: {0}", v ) );
				}
			}

			// Recurse.
			int level = 1;
			HashSet<Vector3D> completedCellCenters = new HashSet<Vector3D>();
			completedCellCenters.Add( first.ID );
			Dictionary<Cell.Edge, int> completedEdges = new Dictionary<Cell.Edge, int>( new Cell.EdgeEqualityComparer() );	// Values are recursion level, so we can save this out.
			HashSet<Sphere> completedFacets = new HashSet<Sphere>( facets.Select( f => f.Sphere ) );
			//if( CellOk( first ) )
				//Util.AddEdges( first, level, completedEdges );
			List<Cell> starting = new List<Cell>();
			starting.Add( first );
			List<Cell> completedCells = new List<Cell>();
			completedCells.Add( first );

			ReflectRecursive( level, starting, completedCellCenters, completedEdges, completedCells, completedFacets );

			bool finite = cellScale != 1;
			completedEdges.Clear();
			SaveToFile( honeycombString, completedEdges, finite );
			PovRay.AppendFacets( completedCells.ToArray(), m_baseDir + honeycombString + ".pov" );
		}

		public static void SaveToFile( string honeycombString, Cell.Edge[] edges, bool finite, bool append = false )
		{
			SaveToFile( honeycombString, edges.ToDictionary( e => e, e => 1 ), finite, append );
		}

		public static void SaveToFile( string honeycombString, Dictionary<Cell.Edge, int> edges, bool finite, bool append = false )
		{
			//H3.Util.LogMinSize( edges );

			if( m_settings.Output == Output.STL )
				MeshEdges( honeycombString, edges, finite );
			else
				PovRay.WriteH3Edges( new PovRay.Parameters()
					{
						AngularThickness = m_settings.AngularThickness,
						Halfspace = m_settings.Halfspace,
						//Halfspace = true,
						ThinEdges = m_settings.ThinEdges,
					},
					edges.Keys.ToArray(), m_baseDir + honeycombString + ".pov", append );
		}

		public static void AppendFacets( string honeycombString, H3.Cell[] cells )
		{
			PovRay.AppendFacets( cells, m_baseDir + honeycombString + ".pov" );
		}

		public static void MeshEdges( string honeycombString, Dictionary<Cell.Edge, int> edges, bool finite )
		{
			Shapeways mesh = new Shapeways();
			if( finite )
			{
				//AddBananas( mesh, edges );
				AddSpheres( mesh, edges );
			}
			else
			{
				foreach( KeyValuePair<Cell.Edge, int> kvp in edges )
					Util.AddToMeshInternal( mesh, kvp.Key.Start, kvp.Key.End );
			}

			mesh.Mesh.Scale( m_settings.Scale );
			//SaveOutEdges( edges, m_baseDir + honeycombString + ".txt" );
			STL.SaveMeshToSTL( mesh.Mesh, m_baseDir + honeycombString + ".stl" );
		}

		/// <summary>
		/// This method is for honeycombs having Euclidean tilings as cells.
		/// Since we can't generate the full cells, these are easier to generate as duals to the other honeycombs.
		/// </summary>
		private static void GenDualHoneycombInternal( Shapeways mesh, Tiling tiling, EHoneycomb honeycomb )
		{
			string honeycombString = Honeycomb.String( honeycomb, true );

			int p, q, r;
			Honeycomb.PQR( honeycomb, out p, out q, out r );

			double inRadius = Honeycomb.InRadius( honeycomb );
			Cell.Facet[] facets = GenFacetSpheres( tiling, inRadius );
			Cell first = new Cell( p, facets );

			// We are already working in the ball model.
			//first = new Cell( p, GenFacets( tiling ) );
			//first.ToSphere();
			//first.ApplyMobius( m_settings.Mobius ); Done in recursive code below.

			// Recurse.
			HashSet<Vector3D> completedCellCenters = new HashSet<Vector3D>();
			Dictionary<Cell.Edge, int> completedEdges = new Dictionary<Cell.Edge, int>( new Cell.EdgeEqualityComparer() );	// Values are recursion level, so we can save this out.
			HashSet<Sphere> completedFacets = new HashSet<Sphere>( facets.Select( f => f.Sphere ) );
			List<Cell> completedCells = new List<Cell>();

			List<Cell> starting = new List<Cell>();
			starting.Add( first );
			completedCellCenters.Add( first.Center );
			completedCells.Add( first );
			int level = 0;
			ReflectRecursiveDual( level, starting, completedCellCenters, completedCells, completedEdges, completedFacets );

			// Can't do this on i33!
			//RemoveDanglingEdgesRecursive( completedEdges );

			//SaveToFile( honeycombString, completedEdges, finite: true );
			PovRay.AppendFacets( completedFacets.ToArray(), m_baseDir + honeycombString + ".pov" );
			//PovRay.AppendFacets( completedCells.ToArray(), m_baseDir + honeycombString + ".pov" );
		}

		public static void RemoveDanglingEdgesRecursive( Dictionary<Cell.Edge, int> edges )
		{
			List<Cell.Edge> needRemoval = new List<Cell.Edge>();

			// Info we'll need to remove dangling edges.
			Dictionary<Vector3D, int> vertexCounts = new Dictionary<Vector3D, int>();
			foreach( Cell.Edge edge in edges.Keys )
			{
				CheckAndAdd( vertexCounts, edge.Start );
				CheckAndAdd( vertexCounts, edge.End );
			}

			foreach( KeyValuePair<Cell.Edge, int> kvp in edges )
			{
				Cell.Edge e = kvp.Key;
				if( vertexCounts[e.Start] == 1 ||
					vertexCounts[e.End] == 1 )
				{
					needRemoval.Add( e );
				}
			}

			if( needRemoval.Count > 0 )
			{
				foreach( Cell.Edge edge in needRemoval )
					edges.Remove( edge );
				RemoveDanglingEdgesRecursive( edges );
			}
		}

		/// <summary>
		/// This will add spheres at incomplete edges.
		/// </summary>
		private static void AddSpheres( Shapeways mesh, Dictionary<Cell.Edge, int> edges )
		{
			Dictionary<Vector3D, int> vertexCounts = new Dictionary<Vector3D, int>();
			foreach( KeyValuePair<Cell.Edge, int> kvp in edges )
			{
				//if( !m_levelsToKeep.Contains( kvp.Value ) )
				//	continue;

				Cell.Edge e = kvp.Key;
				CheckAndAdd( vertexCounts, e.Start );
				CheckAndAdd( vertexCounts, e.End );
			}

			foreach( KeyValuePair<Vector3D, int> kvp in vertexCounts )
			{
				if( kvp.Value < 4 ) // XXX - not always this.
				{
					/*
					Vector3D center = kvp.Key;
					if( m_settings.Halfspace )
						center = H3Models.BallToUHS( center );
					double radius = m_settings.Halfspace ?
						center.Z * Math.Tan( m_settings.Ball_BaseThickness ) : SizeFuncBall( center );
					mesh.AddSphere( center, radius );
					 */

					if( m_settings.ThinEdges )
						mesh.AddSphere( kvp.Key, SizeFuncConst( new Vector3D() ) );
					else
						H3Sphere.AddSphere( mesh, kvp.Key, m_settings );
				}
			}
		}

		/// <summary>
		/// This will add in all our bananas.
		/// </summary>
		private static void AddBananas( Shapeways mesh, Dictionary<Cell.Edge, int> edges )
		{
			foreach( KeyValuePair<Cell.Edge, int> kvp in edges )
			{
				//if( !m_levelsToKeep.Contains( kvp.Value ) )
				//	continue;

				Cell.Edge e = kvp.Key;
				//if( CheckRange( e.Start ) &&
				//	CheckRange( e.End ) )
				//	continue;

				if( m_settings.ThinEdges )
				{
					/*Vector3D[] points = m_settings.Halfspace ?
						H3Models.UHS.GeodesicPoints( e.Start, e.End ) :
						H3Models.Ball.GeodesicPoints( e.Start, e.End );
					mesh.AddCurve( points, SizeFuncConst );*/

					Vector3D center, normal;
					double radius, angleTot;
					int div = 10;
					if( m_settings.Halfspace )
						H3Models.UHS.Geodesic( e.Start, e.End, out center, out radius, out normal, out angleTot );
					else
					{
						H3Models.Ball.Geodesic( e.Start, e.End, out center, out radius, out normal, out angleTot );
						H3Models.Ball.LODThin( e.Start, e.End, out div );
					}
					//mesh.AddArc( center, radius, e.Start, normal, angleTot, div, SizeFuncConst );
					H3.Util.AddToMeshInternal( mesh, e.Start, e.End );
				}
				else
					Banana.AddBanana( mesh, e.Start, e.End, m_settings );

			}
		}

		private static void CheckAndAdd( Dictionary<Vector3D, int> vertexCounts, Vector3D v )
		{
			int count;
			if( vertexCounts.TryGetValue( v, out count ) )
				count++;
			else
				count = 1;

			vertexCounts[v] = count;
		}

		private static void SaveOutEdges( Dictionary<Cell.Edge, int> edges, string fileName )
		{
			using( StreamWriter sw = File.CreateText( fileName ) )
			{
				foreach( KeyValuePair<Cell.Edge, int> kvp in edges )
					kvp.Key.Write( sw, kvp.Value );
			}
		}

		/// <summary>
		/// This method works for honeycombs having cells with a finite number of facets.
		/// The cell vertices may be ideal or finite.
		/// Calculations are carried out in the ball model.
		/// </summary>
		private static void ReflectRecursive( int level, List<Cell> cells, HashSet<Vector3D> completedCellCenters, Dictionary<Cell.Edge, int> completedEdges,
			List<Cell> completedCells, HashSet<Sphere> facets )
		{
			// Breadth first recursion.

			if( 0 == cells.Count )
				return;

			if( level > 2 )
				return;

			level++;

			List<Cell> reflected = new List<Cell>();

			foreach( Cell cell in cells )
			{
				bool idealVerts = cell.IdealVerts;
				List<Sphere> facetSpheres = new List<Sphere>();
				foreach( Cell.Facet facet in cell.Facets )
				{
					facetSpheres.Add( facet.Sphere );
					/*
					if( idealVerts )
						facetSpheres.Add( H3Models.Ball.OrthogonalSphere( facet.Verts[0], facet.Verts[1], facet.Verts[2] ) );
					else
						facetSpheres.Add( H3Models.Ball.OrthogonalSphereInterior( facet.Verts[0], facet.Verts[1], facet.Verts[2] ) );*/
				}

				foreach( Sphere facetSphere in facetSpheres )
				{
					if( completedCellCenters.Count > m_settings.MaxCells )
						return;

					Cell newCell = cell.Clone();
					newCell.Reflect( facetSphere );
					if( completedCellCenters.Contains( newCell.ID ) ||
						!CellOk( newCell ) )
						continue;

					//if( CellOk( newCell ) )
						//Util.AddEdges( newCell, level, completedEdges );
					reflected.Add( newCell );
					completedCellCenters.Add( newCell.ID );
					completedCells.Add( newCell );
					foreach( Cell.Facet facet in newCell.Facets )
						facets.Add( facet.Sphere );
				}
			}

			ReflectRecursive( level, reflected, completedCellCenters, completedEdges, completedCells, facets );
		}

		private static bool CellOk( Cell cell )
		{
			bool idealVerts = cell.IdealVerts;

			// ZZZ - maybe criterion should be total perimeter?
			foreach( Cell.Facet facet in cell.Facets )
			{
				for( int i = 0; i < cell.P; i++ )
				{
					int i1 = i;
					int i2 = i == cell.P - 1 ? 0 : i + 1;
					Vector3D v1 = facet.Verts[i1];
					Vector3D v2 = facet.Verts[i2];

					// Handle the cutoff differently when we are in the ball model,
					// and the cell vertices are finite.
					if( !m_settings.Halfspace && !idealVerts )
					{
						double cutoff = m_settings.Ball_Cutoff;
						if( v1.Abs() > cutoff || v2.Abs() > cutoff )
							return false;
					}
					else
					{
						if( m_settings.Halfspace )
						{
							v1 = H3Models.BallToUHS( v1 );
							v2 = H3Models.BallToUHS( v2 );
							if( ((v1.Z > m_settings.UHS_Horocycle && v2.Z < m_settings.UHS_Horocycle) ||
								 (v2.Z > m_settings.UHS_Horocycle && v1.Z < m_settings.UHS_Horocycle)) &&
								v1.Abs() < m_settings.UHS_MaxBounds &&
								v2.Abs() < m_settings.UHS_MaxBounds )
								return true;
						}
						else
						{
							double dist = v1.Dist( v2 );
							if( dist < m_settings.Ball_MinLength || dist > m_settings.Ball_MaxLength )
								return false;
						}
					}
				}
			}

			return true;
			//return false;	// for some UHS stuff, we reverse this.
		}

		/// <summary>
		/// This is to generate tilings with infinite faceted cells (dual to the input cells).
		/// It works natively in the Ball model.
		/// </summary>c
		private static void ReflectRecursiveDual( int level, List<Cell> cells, HashSet<Vector3D> completedCellCenters,
			List<Cell> completedCells, Dictionary<Cell.Edge, int> completedEdges, HashSet<Sphere> facets )
		{
			// Breadth first recursion.

			if( 0 == cells.Count )
				return;

			level++;

			List<Cell> reflected = new List<Cell>();

			foreach( Cell cell in cells )
			{
				List<Sphere> facetSpheres = new List<Sphere>();
				foreach( Cell.Facet facet in cell.Facets )
				{
					facetSpheres.Add( facet.Sphere );
					//facetSpheres.Add( H3Models.Ball.OrthogonalSphere( facet.Verts[0], facet.Verts[1], facet.Verts[2] ) );
				}

				foreach( Sphere facetSphere in facetSpheres )
				{
					if( completedCellCenters.Count > m_settings.MaxCells )
						return;

					Cell newCell = cell.Clone();
					newCell.Reflect( facetSphere );

					Vector3D start = H3Models.Ball.ApplyMobius( m_settings.Mobius, cell.Center );
					Vector3D end = H3Models.Ball.ApplyMobius( m_settings.Mobius, newCell.Center );

					Cell.Edge edge = new Cell.Edge( start, end );
					if( completedEdges.ContainsKey( edge ) )
						continue;

					// This check was making things not work.
					//if( completedCellCenters.Contains( newCell.Center ) )
					//	continue;

					if( !EdgeOk( edge ) )
						continue;

					reflected.Add( newCell );
					completedCells.Add( newCell );
					completedCellCenters.Add( newCell.Center );
					completedEdges.Add( edge, level );
					foreach( Cell.Facet facet in newCell.Facets )
						facets.Add( facet.Sphere );
				}
			}

			ReflectRecursiveDual( level, reflected, completedCellCenters, completedCells, completedEdges, facets );
		}

		private static bool EdgeOk( Cell.Edge edge )
		{
			double minEdgeLength = m_settings.UHS_MinEdgeLength;
			double maxBounds = m_settings.UHS_MaxBounds;

			if( m_settings.Halfspace )
			{
				Vector3D start = H3Models.BallToUHS( edge.Start );
				Vector3D end = H3Models.BallToUHS( edge.End );
				if( start.Dist( end ) < minEdgeLength )
					return false;

				if( start.Abs() > maxBounds ||
					end.Abs() > maxBounds )
					return false;

				return true;
			}
			else
			{
				//return edge.Start.Dist( edge.End ) > minEdgeLength*.5;

				// Calculated by size testing.  This is the point where the thickness will be .025 inches, assuming we'll scale the ball by a factor of 2.
				// This pushes the limits of Shapeways.
				//double thresh = 0.93;	// {6,3,3}
				//double thresh = 0.972;	// {7,3,3}
				//double thresh = 0.94;
				//double thresh = 0.984;
				//thresh = 0.9975;	// XXX - put in settings.
				double thresh = 0.999;
				return
					edge.Start.Abs() < thresh &&
					edge.End.Abs() < thresh;
			}
		}

		public static double SizeFuncConst( Vector3D v )
		{
			return H3Models.SizeFuncConst( v, m_settings.Scale );
		}
	}
}


text:
https://developer.android.com/studio/projects/add-native-code.html

## encrypt

```
KEY=HOGE
openssl aes-256-cbc -e -in app/google-services.json -out app/encrypted_google-services.json -k $KEY
```

## release

```
export STORE_PASSWORD=XXX
export KEY_PASSWORD=XXX
./gradlew bundleRelease
```

Upload app/build/outputs/bundle/release/bundle.aab

redpajama_set_name: RedPajamaGithub


text:
/* globals describe it beforeEach afterEach */

import { assert } from 'chai'

import store, { flushState } from '../../src/store'

describe('Vuex store: meta #unittest', function () {
  beforeEach(function () {
    store.dispatch('createTestData')
    store.commit('setGradeNameCurrent', '1a')
  })

  afterEach(function () {
    flushState()
  })

  describe('getters', function () {
    it('meta', function () {
      assert.strictEqual(store.getters.meta('teacher'), '')
    })

    it('metaLocation', function () {
      assert.strictEqual(store.getters.metaLocation, '')
    })

    it('metaTeacher', function () {
      assert.strictEqual(store.getters.metaTeacher, '')
    })

    it('metaYear', function () {
      assert.strictEqual(store.getters.metaYear, '')
    })
  })

  describe('mutations', function () {
    it('setMeta', function () {
      store.commit('setMeta', { key: 'teacher', value: 'test' })
      assert.strictEqual(store.getters.meta('teacher'), 'test')
    })
  })
})


text:
This is a port of [Cactus's](https://github.com/koenbok/Cactus) default theme for Jekyll.

## How to use it

Simply clone this repository, and then run `jekyll serve` inside the directory.
This theme is fully compliant with GitHub Pages and their dependencies.
For extra info: [Using Jekyll with Pages](https://help.github.com/articles/using-jekyll-with-pages/#keeping-jekyll-up-to-date).

Cactus theme includes:

- Pagination
- Rss feed
- Code Syntax Highlight
- Author's profile with picture header
- Twitter/Facebook share buttons
- Archive posts list under each post
- Disqus comments

## Screenshots

![Home page](https://raw.githubusercontent.com/nickbalestra/kactus/master/assets/images/Kactus-theme-index.png)
![Post page](https://raw.githubusercontent.com/nickbalestra/kactus/master/assets/images/Kactus-theme-post.png)

## TODO

- [ ] Update highlight.js library and use CDN version
- [ ] Extract colors and fonts to SCSS variables
- [ ] Refactor media queries to be mobile-first


text:
using System.Collections.Generic;
using System.Data.Common;
using Microsoft.AspNetCore.Mvc;
using MySql.Data.MySqlClient;

namespace MySql.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult MySqlData([FromServices] MySqlConnection dbConnection)
        {
            var viewData = new Dictionary<string, string>();
            dbConnection.Open();

            MySqlCommand cmd = new MySqlCommand("SELECT * FROM TestData;", dbConnection);
            DbDataReader rdr = cmd.ExecuteReader();

            while (rdr.Read())
            {
                viewData.Add(rdr[0].ToString(), rdr[1].ToString());
            }
            ViewBag.Database = dbConnection.Database;
            ViewBag.DataSource = dbConnection.DataSource;

            dbConnection.Close();

            return View(viewData);
        }

        public IActionResult Error()
        {
            return View();
        }
    }
}


text:
package com.thesis.expression;

import com.thesis.translator.ExpressionStack;

import java.io.IOException;
import java.io.Writer;

/**
 * Expression that represents a general unconditional jump
 * <p>
 * used for the GOTO instruction
 * It is never printed out directly into the Java code, used as a general jump that can be printed out in the form
 * of either a {@link BreakExpression} or a {@link ContinueExpression}
 */
public class UnconditionalJump extends JumpExpression {

	public UnconditionalJump(int opCode, int jumpLocation) {
		super(opCode, jumpLocation);
		mIsVirtual = true;
	}

	@Override
	public void setThenBranch(ExpressionStack thenBranch) {
		super.setThenBranch(thenBranch);
		mIsVirtual = false;
		mElseBranchEnd = mJumpDestination;
		mStartFrameLocation = mJumpDestination;
	}

	@Override
	public void write(Writer writer) throws IOException {
	}

	@Override
	public void prepareForStack(ExpressionStack stack) {
		// no preparation needed
	}
}


text:
from copy import deepcopy
import logging
from logging.config import dictConfig
import os
import sys
import threading

PALLADIUM_CONFIG_ERROR = """
  Maybe you forgot to set the environment variable PALLADIUM_CONFIG
  to point to your Palladium configuration file?  If so, please
  refer to the manual for more details.
"""

DEFAULT_CONFIG_FILE_LOCATIONS = (
    'palladium-config.py',
    os.path.join('etc', 'palladium-config.py'),
    )

class Config(dict):
    """A dictionary that represents the app's configuration.

    Tries to send a more user friendly message in case of KeyError.
    """
    initialized = False

    def __getitem__(self, name):
        try:
            return super(Config, self).__getitem__(name)
        except KeyError:
            raise KeyError(
                "The required key '{}' was not found in your "
                "configuration. {}".format(name, PALLADIUM_CONFIG_ERROR))

_config = Config()

class ComponentHandler:
    key = '!'

    def __init__(self, config):
        self.config = config
        self.components = []

    def __call__(self, name, props):
        from .util import resolve_dotted_name
        specification = props.copy()
        factory_dotted_name = specification.pop(self.key)
        factory = resolve_dotted_name(factory_dotted_name)
        component = factory(**specification)
        try:
            component.__pld_config_key__ = name
        except AttributeError:
            pass
        self.components.append(component)
        return component

    def finish(self):
        for component in self.components:
            if hasattr(component, 'initialize_component'):
                component.initialize_component(self.config)

class ComponentHandler2(ComponentHandler):
    key = '__factory__'

class CopyHandler:
    key = '__copy__'

    def __init__(self, configs):
        self.configs = configs

    @staticmethod
    def _resolve(configs, dotted_path):
        for config in configs[::-1]:
            value = config
            for part in dotted_path.split('.'):
                try:
                    value = value[part]
                except KeyError:
                    break
            else:
                return value
        else:
            raise KeyError(dotted_path)

    def __call__(self, name, props):
        dotted_path = props[self.key]
        try:
            value = self._resolve(self.configs[-1:], dotted_path)
            self_reference = value is props
        except KeyError:
            self_reference = False

        if self_reference:
            value = self._resolve(self.configs[:-1], dotted_path)
        else:
            try:
                value = self._resolve(self.configs, dotted_path)
            except KeyError:
                if '__default__' in props:
                    return props['__default__']
                else:
                    raise

        value = deepcopy(value)
        nonmagicprops = [
            prop for prop in props
            if not (prop.startswith('__') and prop.endswith('__'))
            ]
        if nonmagicprops:
            recursive_copy = self.key in value
            value.update(props)
            if not recursive_copy:
                del value[self.key]
        return value

class PythonHandler:
    key = '__python__'

    def __init__(self, config):
        self.config = config

    def __call__(self, name, props):
        statements = props.pop(self.key)
        exec(
            statements,
            globals(),
            {key: self.config for key in ['C', 'cfg', 'config']},
            )
        return props

def rewrite_handler(key_from, key_to):
    class RewriteHandler:
        key = key_from
        target = key_to

        def __init__(self, config):
            pass

        def __call__(self, name, props):
            props[self.target] = props.pop(self.key)
            return props
    return RewriteHandler

def _handlers_phase0(configs):
    return {
        Handler.key: Handler(configs) for Handler in [
            rewrite_handler('__factory__', '!'),
            CopyHandler,
            ]
        }

def _handlers_phase1(config):
    return {
        Handler.key: Handler(config) for Handler in [
            PythonHandler,
            ]
        }

def _handlers_phase2(config):
    return {
        Handler.key: Handler(config) for Handler in [
            ComponentHandler,
            ]
        }

def _run_config_handlers_recursive(props, handlers):
    if isinstance(props, dict):
        for key, value in tuple(props.items()):
            if isinstance(value, dict):
                _run_config_handlers_recursive(value, handlers)
                for name, handler in handlers.items():
                    if name in value:
                        value = props[key] = handler(key, value)
            elif isinstance(value, (list, tuple)):
                _run_config_handlers_recursive(value, handlers)
    elif isinstance(props, (list, tuple)):
        for i, item in enumerate(props):
            if isinstance(item, dict):
                _run_config_handlers_recursive(item, handlers)
                for name, handler in handlers.items():
                    if name in item:
                        item = props[i] = handler(str(i), item)
            elif isinstance(item, (list, tuple)):
                _run_config_handlers_recursive(item, handlers)

def _run_config_handlers(config, handlers):
    wrapped_config = {'root': config}
    _run_config_handlers_recursive(wrapped_config, handlers)
    for handler in handlers.values():
        if hasattr(handler, 'finish'):
            handler.finish()
    return wrapped_config['root']

def _initialize_logging(config):
    if 'logging' in config:
        dictConfig(config['logging'])
    else:
        logging.basicConfig(level=logging.DEBUG)

def process_config(
    *configs,
    handlers0=_handlers_phase0,
    handlers1=_handlers_phase1,
    handlers2=_handlers_phase2
):
    config_final = {}

    for config in configs:
        config_org = deepcopy(config_final)
        config_final.update(config)
        _run_config_handlers(
            config_final, handlers0([config_org, config]))
        _run_config_handlers(
            config_final, handlers0([config_final, {}]))

    _run_config_handlers(config_final, handlers1(config_final))
    _run_config_handlers(config_final, handlers2(config_final))
    _initialize_logging(config_final)
    return config_final

_get_config_lock = threading.RLock()

def get_config(**extra):
    with _get_config_lock:
        config = _get_config(**extra)
    return config

def _get_config(**extra):
    if not _config.initialized:
        _config.update(extra)
        _config.initialized = True

        fnames = os.environ.get('PALLADIUM_CONFIG')
        if fnames is None:
            for fname in DEFAULT_CONFIG_FILE_LOCATIONS:
                if os.path.exists(fname):  # pragma: no cover
                    fnames = fname
                    print("Using configuration at {}".format(fname))
                    break

        if fnames is not None:
            configs = []
            fnames = [fname.strip() for fname in fnames.split(',')]
            for fname in fnames:
                sys.path.insert(0, os.path.dirname(fname))
                with open(fname) as f:
                    config = eval(f.read(), {
                        'environ': os.environ,
                        'here': os.path.abspath(os.path.dirname(fname)),
                        })
                configs.append(config)
            _config.update(process_config(_config, *configs))
    return _config

def initialize_config(**extra):
    if _config.initialized:
        raise RuntimeError("Configuration was already initialized")
    return get_config(**extra)


text:
`LoginScreen` adds login, registration and reset password features to your iOS and Android applications.

### Examples
#### Implementing credential operation's logic:
To implement your credential operation's logic you need to implement interface `ICredentialsProvider`:

```csharp
using LoginScreen;

public class CredentialsProvider : ICredentialsProvider
{
	// Constructor without parameters is required

	public bool NeedLoginAfterRegistration {
		get {
			// If you want your user to login after he/she has been registered
			return true;

			// Otherwise you can:
			// return false;
		}
	}

	public void Login (string userName, string password, Action successCallback, Action<LoginScreenFaultDetails> failCallback)
	{
		// Do some operations to login user

		// If login was successfully completed
		successCallback();

		// Otherwise
		// failCallback(new LoginScreenFaultDetails {
		// 	CommonErrorMessage = "Some error message relative to whole form",
		// 	UserNameErrorMessage = "Some error message relative to user name form field",
		// 	PasswordErrorMessage = "Some error message relative to password form field"
		// });
	}

	public void Register (string email, string userName, string password, Action successCallback, Action<LoginScreenFaultDetails> failCallback)
	{
		// Do some operations to register user

		// If registration was successfully completed
		successCallback();

		// Otherwise
		// failCallback(new LoginScreenFaultDetails {
		// 	CommonErrorMessage = "Some error message relative to whole form",
		// 	EmailErrorMessage = "Some error message relative to e-mail form field",
		// 	UserNameErrorMessage = "Some error message relative to user name form field",
		// 	PasswordErrorMessage = "Some error message relative to password form field"
		// });
	}

	public void ResetPassword (string email, Action successCallback, Action<LoginScreenFaultDetails> failCallback)
	{
		// Do some operations to reset user's password

		// If password was successfully reset
		successCallback();

		// Otherwise
		// failCallback(new LoginScreenFaultDetails {
		// 	CommonErrorMessage = "Some error message relative to whole form",
		// 	EmailErrorMessage = "Some error message relative to e-mail form field"
		// });
	}

	public bool ShowPasswordResetLink {
		get {
			// If you want your login screen to have a forgot password button
			return true;

			// Otherwise you can:
			// return false;
		}
	}

	public bool ShowRegistration {
		get {
			// If you want your login screen to have a register new user button
			return true;

			// Otherwise you can:
			// return false;
		}
	}
}
```

You must call one of callbacks; otherwise login screen will never hide.

#### Adding `LoginScreen` to your app:
- iOS app

```csharp
using LoginScreen;

private bool needLogin = true;

public override void ViewDidAppear (bool animated)
{
	base.ViewDidAppear (animated);

	if (needLogin) {
		LoginScreenControl<CredentialsProvider>.Activate (this);
		needLogin = false;
	}
}
```

- Android app

```csharp
using LoginScreen;

protected override void OnCreate (Bundle bundle)
{
	base.OnCreate (bundle);

	// ...

	LoginScreenControl<CredentialsProvider>.Activate (this);
}
```

#### Localization

To localize `LoginScreen` or change strings from default you need to implement `ILoginScreenMessages` interface:

```csharp
using LoginScreen;

public class LoginScreenMessages : ILoginScreenMessages
{
	// Constructor without parameters is required

	// There must be placed properties that returns some sctrings to use it to display
}
```

And then, you neet to use `LoginScreenControl<CredentialsProvider, LoginScreenMessages>` instead of `LoginScreenControl<CredentialsProvider>`.

*Screenshots assembled with [PlaceIt](http://placeit.breezi.com).*


text:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

#include <linux/sipc.h>
#include <linux/stty.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/tty.h>
#include <linux/vt_kern.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/delay.h>

#include <linux/compat.h>
#include <linux/tty_flip.h>
#include <linux/kthread.h>

#include <linux/sprd_2351.h>

#define STTY_DEV_MAX_NR 	1
#define STTY_MAX_DATA_LEN 		4096

#define STTY_STATE_OPEN 	1
#define STTY_STATE_CLOSE 	0

struct stty_device {
	struct stty_init_data	*pdata;
	struct tty_port 		*port;
	struct tty_struct 		*tty;
	struct tty_driver 		*driver;

	/* stty state */
	uint32_t 		state;
	struct mutex 		stat_lock;
};

static void stty_handler (int event, void* data)
{
	struct stty_device *stty = data;
	int i, cnt = 0;
	unsigned char buf[STTY_MAX_DATA_LEN] = {0};

	pr_debug("stty handler event=%d \n", event);

	switch(event) {
		case SBUF_NOTIFY_WRITE:
			break;
		case SBUF_NOTIFY_READ:
			cnt = sbuf_read(stty->pdata->dst, stty->pdata->channel,
					stty->pdata->bufid,(void *)buf, STTY_MAX_DATA_LEN, 0);
			pr_debug("stty handler read data len =%d \n", cnt);
			mutex_lock(&(stty->stat_lock));
			if ((stty->state == STTY_STATE_OPEN) && (cnt > 0)) {
				for(i = 0; i < cnt; i++) {
					tty_insert_flip_char(stty->port, buf[i], TTY_NORMAL);
				}
				tty_schedule_flip(stty->port);
			}
			mutex_unlock(&(stty->stat_lock));
			break;
		default:
			printk(KERN_ERR "Received event is invalid(event=%d)\n", event);
	}

	return;
}

static int stty_open(struct tty_struct *tty, struct file * filp)
{
	struct stty_device *stty = NULL;
	struct tty_driver *driver = NULL;

	if (tty == NULL) {
		printk(KERN_ERR "stty open input tty is NULL!\n");
		return -ENOMEM;
	}
	driver = tty->driver;
	stty = (struct stty_device *)driver->driver_state;

	if(stty == NULL) {
		printk(KERN_ERR "stty open input stty NULL!\n");
		return -ENOMEM;
	}

	if (sbuf_status(stty->pdata->dst, stty->pdata->channel) != 0) {
		printk(KERN_ERR "stty_open sbuf not ready to open!dst=%d,channel=%d\n"
			,stty->pdata->dst,stty->pdata->channel);
		return -ENODEV;
	}
	stty->tty = tty;
	tty->driver_data = (void *)stty;

	mutex_lock(&(stty->stat_lock));
	stty->state = STTY_STATE_OPEN;
	mutex_unlock(&(stty->stat_lock));

#if defined(CONFIG_MACH_SP7730EC) || defined(CONFIG_MACH_SP7730GA) || defined(CONFIG_MACH_SPX35EC) || defined(CONFIG_MACH_SP8830GA) \
    || defined(CONFIG_MACH_SP7715EA) || defined(CONFIG_MACH_SP7715EATRISIM) || defined(CONFIG_MACH_SP7715GA) || defined(CONFIG_MACH_SP7715GATRISIM)||defined(CONFIG_MACH_SP5735C1EA)
    rf2351_gpio_ctrl_power_enable(1);
#endif
	pr_debug("stty_open device success! \n");

	return 0;
}

static void stty_close(struct tty_struct *tty, struct file * filp)
{
	struct stty_device *stty = NULL;

	if (tty == NULL) {
		printk(KERN_ERR "stty close input tty is NULL!\n");
		return;
	}
	stty = (struct stty_device *) tty->driver_data;
	if (stty == NULL) {
		printk(KERN_ERR "stty close s tty is NULL!\n");
		return;
	}

	mutex_lock(&(stty->stat_lock));
	stty->state = STTY_STATE_CLOSE;
	mutex_unlock(&(stty->stat_lock));

	pr_debug("stty_close device success !\n");

    #if defined(CONFIG_MACH_SP7730EC) || defined(CONFIG_MACH_SP7730GA) || defined(CONFIG_MACH_SPX35EC) || defined(CONFIG_MACH_SP8830GA) \
        || defined(CONFIG_MACH_SP7715EA) || defined(CONFIG_MACH_SP7715EATRISIM) || defined(CONFIG_MACH_SP7715GA) || defined(CONFIG_MACH_SP7715GATRISIM)||defined(CONFIG_MACH_SP5735C1EA)
    rf2351_gpio_ctrl_power_enable(0);
    #endif

	return;
}

static int stty_write(struct tty_struct * tty,
	      const unsigned char *buf, int count)
{
	struct stty_device *stty = tty->driver_data;
	int cnt;
	pr_debug("stty write count=%d \n", count);

	cnt = sbuf_write(stty->pdata->dst, stty->pdata->channel,
					stty->pdata->bufid, (void *)buf, count, -1);
	return cnt;
}

static void stty_flush_chars(struct tty_struct *tty)
{
	return;
}

static int stty_write_room(struct tty_struct *tty)
{
	return INT_MAX;
}

static const struct tty_operations stty_ops = {
	.open  = stty_open,
	.close = stty_close,
	.write = stty_write,
	.flush_chars = stty_flush_chars,
	.write_room  = stty_write_room,
};

static struct tty_port *stty_port_init(void)
{
	struct tty_port *port = NULL;

	port = kzalloc(sizeof(struct tty_port),GFP_KERNEL);
	if (port == NULL) {
		printk(KERN_ERR "stty_port_init Failed to allocate device!\n");
		return NULL;
	}
	tty_port_init(port);
	return port;
}

static int stty_driver_init(struct stty_device *device)
{
	struct tty_driver *driver;
	int ret = 0;

	mutex_init(&(device->stat_lock));

	device->port = stty_port_init();
	if (!device->port) {
		return -ENOMEM;
	}

	driver = alloc_tty_driver(STTY_DEV_MAX_NR);
	if (!driver)
		return -ENOMEM;
	/*
	 * Initialize the tty_driver structure
	 * Entries in stty_driver that are NOT initialized:
	 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
	 */
	driver->owner = THIS_MODULE;
	driver->driver_name = device->pdata->name;
	driver->name = device->pdata->name;
	driver->major = 0;
	driver->type = TTY_DRIVER_TYPE_SYSTEM;
	driver->subtype = SYSTEM_TYPE_TTY;
	driver->init_termios = tty_std_termios;
	driver->driver_state = (void*)device;
	device->driver = driver;
	 /* initialize the tty driver */
	tty_set_operations(driver, &stty_ops);
	tty_port_link_device(device->port, driver, 0);
	ret = tty_register_driver(driver);
	if (ret) {
		put_tty_driver(driver);
		tty_port_destroy(device->port);
		return ret;
	}
	return ret;
}

static void stty_driver_exit(struct stty_device *device)
{
	struct tty_driver *driver = device->driver;
	tty_unregister_driver(driver);
	tty_port_destroy(device->port);
}

static int  stty_probe(struct platform_device *pdev)
{
	struct stty_init_data *pdata = (struct stty_init_data*)pdev->dev.platform_data;
	struct stty_device *stty;
	int rval= 0;

	stty = kzalloc(sizeof(struct stty_device),GFP_KERNEL);
	if (stty == NULL) {
		printk(KERN_ERR "stty Failed to allocate device!\n");
		return -ENOMEM;
	}

	stty->pdata = pdata;
	rval = stty_driver_init(stty);
	if (rval) {
		kfree(stty);
		printk(KERN_ERR "stty driver init error!\n");
		return -EINVAL;
	}

	rval = sbuf_register_notifier(pdata->dst, pdata->channel,
					pdata->bufid, stty_handler, stty);
	if (rval) {
		stty_driver_exit(stty);
		kfree(stty->port);
		kfree(stty);
		printk(KERN_ERR "regitster notifier failed (%d)\n", rval);
		return rval;
	}

	printk( "stty_probe init device addr: %p\n", (void *)stty);
	platform_set_drvdata(pdev, stty);

	return 0;
}

static int  stty_remove(struct platform_device *pdev)
{
	struct stty_device *stty = platform_get_drvdata(pdev);
	int rval;

	rval = sbuf_register_notifier(stty->pdata->dst, stty->pdata->channel,
					stty->pdata->bufid, NULL, NULL);
	if (rval) {
		printk(KERN_ERR " unregitster notifier failed (%d)\n", rval);
		return rval;
	}

	stty_driver_exit(stty);
	kfree(stty->port);
	kfree(stty);
	platform_set_drvdata(pdev, NULL);
	return 0;
}

static struct platform_driver stty_driver = {
	.driver = {
		.owner = THIS_MODULE,
		.name = "sttybt",
	},
	.probe = stty_probe,
	.remove = stty_remove,
};

static int __init stty_init(void)
{
	return platform_driver_register(&stty_driver);
}

static void __exit stty_exit(void)
{
	platform_driver_unregister(&stty_driver);
}

late_initcall(stty_init);
module_exit(stty_exit);

MODULE_AUTHOR("Dewu Jiang");
MODULE_DESCRIPTION("SIPC/stty driver");
MODULE_LICENSE("GPL");


text:
import chalk =  require("chalk");
import { take, select } from "redux-saga/effects";
import path = require("path");
import moment = require("moment");
import { titleize } from "inflection";
import { parallel } from "mesh";
import { weakMemo } from "../memo";
import AnsiUp from "ansi_up";
import { reader } from "../monad";
import { noop } from "lodash";
import { ImmutableObject, createImmutableObject } from "../immutable";
import { LogLevel, LogAction, LogActionTypes, Logger } from "./base";

// beat TS type checking
chalk.enabled = true;

function createLogColorizer(tester: RegExp, replaceValue: any) {
  return function(input: string) {
    if (!tester.test(input)) return input;
    return input.replace(tester, replaceValue);
  }
}

export type ConsoleLogState = {
  argv?: {
    color?: boolean,
    hlog?: boolean
  },
  log?: {
    level: LogLevel,
    prefix?: string
  }
}

const cwd = process.cwd();

const highlighters = [

  createLogColorizer(/^INF/, (match) => chalk.bgCyan(match)),
  createLogColorizer(/^ERR/, (match) => chalk.bgRed(match)),
  createLogColorizer(/^DBG/, (match) => chalk.grey.bgBlack(match)),
  createLogColorizer(/^WRN/, (match) => chalk.bgYellow(match)),

  // timestamp
  createLogColorizer(/\[\d+\.\d+\.\d+\]/, (match, inner) => `[${chalk.grey(inner)}]`),

  // URL
  createLogColorizer(/((\w{3,}\:\/\/)|([^\/\s\("':]+)?\/)([^\/\)\s"':]+\/?)+/g, (match) => {
    return chalk.yellow(/\w+:\/\//.test(match) ? match : match.replace(cwd + "/", ""))
  }),

  // duration
  createLogColorizer(/\s\d+(\.\d+)?(s|ms|m|h|d)(\s|$)/g, (match) => chalk.bold.cyan(match)),

  // numbers
  createLogColorizer(/\b\d+(\.\d+)?\b/g, (match, inner) => `${chalk.cyan(match)}`),

  // strings
  createLogColorizer(/"(.*?)"/g, (match, inner) => `"${chalk.blue(inner)}"`),

  // tokens
  createLogColorizer(/([\:\{\}",\(\)]|->|null|undefined|Infinity)/g, (match) => chalk.grey(match)),

 // <<output - green (from audio again)
  createLogColorizer(/<<(.*)/g, (match, word) => chalk.green(word)),

 // >>input - magenta (from audio)
  createLogColorizer(/>>(.*)/g, (match, word) => chalk.magenta(word)),

  // **BIG EMPHASIS**
  createLogColorizer(/\*\*(.*?)\*\*/, (match, word) => chalk.bgBlue(word)),

  // *emphasis*
  createLogColorizer(/\*(.*?)\*/g, (match, word) => chalk.bold(word)),

  // ___underline___
  createLogColorizer(/___(.*?)___/g, (match, word) => chalk.underline(word)),

  // ~de emphasis~
  createLogColorizer(/~(.*?)~/g, (match, word) => chalk.grey(word)),
];

function colorize(input: string) {
  let output = input;
  for (let i = 0, n = highlighters.length; i < n; i++) output = highlighters[i](output);
  return output;
}

function styledConsoleLog(...args: any[]) {
    var argArray = [];

    if (args.length) {
        var startTagRe = /<span\s+style=(['"])([^'"]*)\1\s*>/gi;
        var endTagRe = /<\/span>/gi;

        var reResultArray;
        argArray.push(arguments[0].replace(startTagRe, '%c').replace(endTagRe, '%c'));
        while (reResultArray = startTagRe.exec(arguments[0])) {
            argArray.push(reResultArray[2]);
            argArray.push('');
        }

        // pass through subsequent args since chrome dev tools does not (yet) support console.log styling of the following form: console.log('%cBlue!', 'color: blue;', '%cRed!', 'color: red;');
        for (var j = 1; j < arguments.length; j++) {
            argArray.push(arguments[j]);
        }
    }

    console.log.apply(console, argArray);
}

// I'm against abbreviations, but it's happening here
// since all of these are the same length -- saves space in stdout, and makes
// logs easier to read.
const PREFIXES = {
  [LogLevel.DEBUG]: "DBG ",
  [LogLevel.INFO]: "INF ",
  [LogLevel.WARNING]: "WRN ",
  [LogLevel.ERROR]: "ERR ",
};
const defaultState = { level: LogLevel.ALL, prefix: "" };

export function* consoleLogSaga() {

  while(true) {
    const { log: { level: acceptedLevel, prefix }}: ConsoleLogState = (yield select()) || defaultState;
    let { text, level }: LogAction = (yield take(LogActionTypes.LOG));
    if (!(acceptedLevel & level)) continue;
    const log = {
      [LogLevel.DEBUG]: console.log.bind(console),
      [LogLevel.LOG]: console.log.bind(console),
      [LogLevel.INFO]: console.info.bind(console),
      [LogLevel.WARNING]: console.warn.bind(console),
      [LogLevel.ERROR]: console.error.bind(console)
    }[level];

    text = PREFIXES[level] + (prefix || "") + text;
    text = colorize(text);

    if (typeof window !== "undefined" && !window["$synthetic"]) {
      return styledConsoleLog(new AnsiUp().ansi_to_html(text));
    }

    log(text);
  }
}


text:
<?php
Class PageController extends Controller {
    public $layout = 'column1';
    public function actionIndex () {

        $this->render('index');
    }
}


text:
module Oceanography
  class CommentsMapper

    COMMENT_REGEX = /^comment(\d{2})?$/ui

    # Accepts flat hash merging all 'commentXX's keys to a 'comments' array
    def map(doc, nc_hash = {})
      doc.each_with_object({}) do |(key,value), hash|
        if key =~ COMMENT_REGEX
          hash["comments"] ||= []
          hash["comments"].push(value)
        else
          hash[key] = value
        end
      end
    end
  end
end


text:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BuzzFizz: BuzzFizz Directory Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

BuzzFizz

An application problem

Main Page

Related Pages

Files

File List

Globals

BuzzFizz

BuzzFizz Directory Reference

Directories

<address class="footer">
Generated by

 1.8.11
</address>


text:
using DevExpress.Xpo;

namespace MemberIdentification.DatabaseCode
{

    public partial class Persona : XPLiteObject
    {
        string fCity;
        [Size(255)]
        public string City
        {
            get { return this.fCity; }
            set { this.SetPropertyValue<string>("City", ref this.fCity, value); }
        }
        string fCompany;
        [Size(255)]
        public string Company
        {
            get { return this.fCompany; }
            set { this.SetPropertyValue<string>("Company", ref this.fCompany, value); }
        }
        int fId;
        [Key(true)]
        public int Id
        {
            get { return this.fId; }
            set { this.SetPropertyValue<int>("Id", ref this.fId, value); }
        }
        string fInitials;
        [Size(255)]
        public string Initials
        {
            get { return this.fInitials; }
            set { this.SetPropertyValue<string>("Initials", ref this.fInitials, value); }
        }
        string fOccupation;
        [Size(255)]
        public string Occupation
        {
            get { return this.fOccupation; }
            set { this.SetPropertyValue<string>("Occupation", ref this.fOccupation, value); }
        }
        string fTid;
        [Size(255)]
        public string Tid
        {
            get { return this.fTid; }
            set { this.SetPropertyValue<string>("Tid", ref this.fTid, value); }
        }
        int fSawTimes;
        public int SawTimes
        {
            get { return this.fSawTimes; }
            set { this.SetPropertyValue<int>("SawTimes", ref this.fSawTimes, value); }
        }
    }

}


text:
package org.apache.batik.extension.svg;

import java.awt.Color;
import java.awt.font.TextAttribute;
import java.awt.geom.Point2D;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.batik.bridge.Bridge;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.BridgeException;
import org.apache.batik.bridge.CSSUtilities;
import org.apache.batik.bridge.CursorManager;
import org.apache.batik.bridge.SVGAElementBridge;
import org.apache.batik.bridge.SVGTextElementBridge;
import org.apache.batik.bridge.SVGUtilities;
import org.apache.batik.bridge.TextUtilities;
import org.apache.batik.bridge.UnitProcessor;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.dom.events.NodeEventTarget;
import org.apache.batik.dom.util.XLinkSupport;
import org.apache.batik.dom.util.XMLSupport;
import org.apache.batik.dom.svg.SVGOMElement;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.TextNode;
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
import org.apache.batik.gvt.text.TextPath;
import org.apache.batik.gvt.text.TextPaintInfo;
import org.apache.batik.util.SVGConstants;
import org.apache.batik.util.XMLConstants;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Bridge class for the <flowText> element.
 *
 * @author Thomas DeWeese
 * @version $Id: BatikFlowTextElementBridge.java 579031 2007-09-25 01:51:52Z cam $
 */
public class BatikFlowTextElementBridge extends SVGTextElementBridge
    implements BatikExtConstants {

    public static final AttributedCharacterIterator.Attribute FLOW_PARAGRAPH
        = GVTAttributedCharacterIterator.TextAttribute.FLOW_PARAGRAPH;

    public static final AttributedCharacterIterator.Attribute
        FLOW_EMPTY_PARAGRAPH
        = GVTAttributedCharacterIterator.TextAttribute.FLOW_EMPTY_PARAGRAPH;

    public static final AttributedCharacterIterator.Attribute FLOW_LINE_BREAK
        = GVTAttributedCharacterIterator.TextAttribute.FLOW_LINE_BREAK;

    public static final AttributedCharacterIterator.Attribute FLOW_REGIONS
        = GVTAttributedCharacterIterator.TextAttribute.FLOW_REGIONS;

    public static final AttributedCharacterIterator.Attribute PREFORMATTED
        = GVTAttributedCharacterIterator.TextAttribute.PREFORMATTED;

    /**
     * Constructs a new bridge for the <flowText> element.
     */
    public BatikFlowTextElementBridge() {}

    /**
     * Returns the SVG namespace URI.
     */
    public String getNamespaceURI() {
        return BATIK_12_NAMESPACE_URI;
    }

    /**
     * Returns 'flowText'.
     */
    public String getLocalName() {
        return BATIK_EXT_FLOW_TEXT_TAG;
    }

    /**
     * Returns a new instance of this bridge.
     */
    public Bridge getInstance() {
        return new BatikFlowTextElementBridge();
    }

    /**
     * Returns false as text is not a container.
     */
    public boolean isComposite() {
        return false;
    }

    protected GraphicsNode instantiateGraphicsNode() {
        return new FlowExtTextNode();
    }

    /**
     * Returns the text node location In this case the text node may
     * have serveral effective locations (one for each flow region).
     * So it always returns 0,0.
     *
     * @param ctx the bridge context to use
     * @param e the text element
     */
    protected Point2D getLocation(BridgeContext ctx, Element e) {
        return new Point2D.Float(0,0);
    }

    protected void addContextToChild(BridgeContext ctx,Element e) {
        if (getNamespaceURI().equals(e.getNamespaceURI())) {
            String ln = e.getLocalName();
            if (ln.equals(BATIK_EXT_FLOW_PARA_TAG) ||
                ln.equals(BATIK_EXT_FLOW_REGION_BREAK_TAG) ||
                ln.equals(BATIK_EXT_FLOW_LINE_TAG) ||
                ln.equals(BATIK_EXT_FLOW_SPAN_TAG) ||
                ln.equals(SVG_A_TAG) ||
                ln.equals(SVG_TREF_TAG)) {
                ((SVGOMElement) e).setSVGContext
                    (new BatikFlowContentBridge(ctx, this, e));
            }
        }

        // traverse the children to add SVGContext
        Node child = getFirstChild(e);
        while (child != null) {
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                addContextToChild(ctx,(Element)child);
            }
            child = getNextSibling(child);
        }
    }
    /**
     * Creates the attributed string which represents the given text
     * element children.
     *
     * @param ctx the bridge context to use
     * @param element the text element
     */
    protected AttributedString buildAttributedString(BridgeContext ctx,
                                                     Element element) {
        List rgns = getRegions(ctx, element);
        AttributedString ret = getFlowDiv(ctx, element);
        if (ret == null) return ret;

        ret.addAttribute(FLOW_REGIONS, rgns, 0, 1);
        return ret;
    }

    /**
     * Adds glyph position attributes to an AttributedString.
     */
    protected void addGlyphPositionAttributes(AttributedString as,
                                              Element element,
                                              BridgeContext ctx) {
        if (element.getNodeType()     != Node.ELEMENT_NODE) return;
        String eNS = element.getNamespaceURI();
        if ((!eNS.equals(getNamespaceURI())) &&
            (!eNS.equals(SVG_NAMESPACE_URI)))
            return;
        if (element.getLocalName()    != BATIK_EXT_FLOW_TEXT_TAG) {
            // System.out.println("Elem: " + element);
            super.addGlyphPositionAttributes(as, element, ctx);
            return;
        }

        for (Node n = element.getFirstChild();
             n != null; n = n.getNextSibling()) {
            if (n.getNodeType()     != Node.ELEMENT_NODE) continue;
            String nNS = n.getNamespaceURI();
            if ((!getNamespaceURI().equals(nNS)) &&
                (!SVG_NAMESPACE_URI.equals(nNS))) {
                continue;
            }
            Element e = (Element)n;
            String ln = e.getLocalName();
            if (ln.equals(BATIK_EXT_FLOW_DIV_TAG)) {
                // System.out.println("D Elem: " + e);
                super.addGlyphPositionAttributes(as, e, ctx);
                return;
            }
        }
    }

    protected void addChildGlyphPositionAttributes(AttributedString as,
                                                   Element element,
                                                   BridgeContext ctx) {
        // Add Paint attributres for children of text element
        for (Node child = element.getFirstChild();
             child != null;
             child = child.getNextSibling()) {
            if (child.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            String cNS = child.getNamespaceURI();
            if ((!getNamespaceURI().equals(cNS)) &&
                (!SVG_NAMESPACE_URI.equals(cNS))) {
                continue;
            }
            String ln = child.getLocalName();
            if (ln.equals(BATIK_EXT_FLOW_PARA_TAG) ||
                ln.equals(BATIK_EXT_FLOW_REGION_BREAK_TAG) ||
                ln.equals(BATIK_EXT_FLOW_LINE_TAG) ||
                ln.equals(BATIK_EXT_FLOW_SPAN_TAG) ||
                ln.equals(SVG_A_TAG) ||
                ln.equals(SVG_TREF_TAG)) {
                addGlyphPositionAttributes(as, (Element)child, ctx);
            }
        }
    }

    /**
     * Adds painting attributes to an AttributedString.
     */
    protected void addPaintAttributes(AttributedString as,
                                      Element element,
                                      TextNode node,
                                      TextPaintInfo parentPI,
                                      BridgeContext ctx) {
        if (element.getNodeType() != Node.ELEMENT_NODE) return;
        String eNS = element.getNamespaceURI();
        if ((!eNS.equals(getNamespaceURI())) &&
            (!eNS.equals(SVG_NAMESPACE_URI)))
            return;
        if (element.getLocalName()    != BATIK_EXT_FLOW_TEXT_TAG) {
            // System.out.println("Elem: " + element);
            super.addPaintAttributes(as, element, node, parentPI, ctx);
            return;
        }

        for (Node n = element.getFirstChild();
             n != null; n = n.getNextSibling()) {
            if (n.getNodeType()     != Node.ELEMENT_NODE) continue;
            if (!getNamespaceURI().equals(n.getNamespaceURI())) continue;
            Element e = (Element)n;
            String ln = e.getLocalName();
            if (ln.equals(BATIK_EXT_FLOW_DIV_TAG)) {
                // System.out.println("D Elem: " + e);
                super.addPaintAttributes(as, e, node, parentPI, ctx);
                return;
            }
        }
    }

    protected void addChildPaintAttributes(AttributedString as,
                                           Element element,
                                           TextNode node,
                                           TextPaintInfo parentPI,
                                           BridgeContext ctx) {
        // Add Paint attributres for children of text element
        for (Node child = element.getFirstChild();
             child != null;
             child = child.getNextSibling()) {
            if (child.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            String cNS = child.getNamespaceURI();
            if ((!getNamespaceURI().equals(cNS)) &&
                (!SVG_NAMESPACE_URI.equals(cNS))) {
                continue;
            }
            String ln = child.getLocalName();
            if (ln.equals(BATIK_EXT_FLOW_PARA_TAG) ||
                ln.equals(BATIK_EXT_FLOW_REGION_BREAK_TAG) ||
                ln.equals(BATIK_EXT_FLOW_LINE_TAG) ||
                ln.equals(BATIK_EXT_FLOW_SPAN_TAG) ||
                ln.equals(SVG_A_TAG) ||
                ln.equals(SVG_TREF_TAG)) {
                Element childElement = (Element)child;
                TextPaintInfo pi = getTextPaintInfo(childElement, node,
                                                    parentPI, ctx);
                addPaintAttributes(as, childElement, node, pi, ctx);
            }
        }
    }

    protected AttributedString getFlowDiv
        (BridgeContext ctx, Element element) {
        for (Node n = element.getFirstChild();
             n != null; n = n.getNextSibling()) {
            if (n.getNodeType()     != Node.ELEMENT_NODE) continue;
            if (!getNamespaceURI().equals(n.getNamespaceURI())) continue;
            Element e = (Element)n;

            String ln = n.getLocalName();
            if (ln.equals(BATIK_EXT_FLOW_DIV_TAG)) {
                return gatherFlowPara(ctx, e);
            }
        }
        return null;
    }

    protected AttributedString gatherFlowPara
        (BridgeContext ctx, Element div) {
        TextPaintInfo divTPI = new TextPaintInfo();
        // Set some basic props so we can get bounds info for complex paints.
        divTPI.visible   = true;
        divTPI.fillPaint = Color.black;
        elemTPI.put(div, divTPI);

        AttributedStringBuffer asb = new AttributedStringBuffer();
        List paraEnds  = new ArrayList();
        List paraElems = new ArrayList();
        List lnLocs    = new ArrayList();
        for (Node n = div.getFirstChild();
             n != null;
             n = n.getNextSibling()) {

            if (n.getNodeType() != Node.ELEMENT_NODE
                    || !getNamespaceURI().equals(n.getNamespaceURI())) {
                continue;
            }
            Element e = (Element)n;

            String ln = e.getLocalName();
            if (ln.equals(BATIK_EXT_FLOW_PARA_TAG)) {
                fillAttributedStringBuffer
                    (ctx, e, true, null, null, asb, lnLocs);

                paraElems.add(e);
                paraEnds.add(new Integer(asb.length()));
            } else if (ln.equals(BATIK_EXT_FLOW_REGION_BREAK_TAG)) {
                fillAttributedStringBuffer
                        (ctx, e, true, null, null, asb, lnLocs);

                paraElems.add(e);
                paraEnds.add(new Integer(asb.length()));
            }
        }
        divTPI.startChar = 0;
        divTPI.endChar   = asb.length()-1;

        // Layer in the PARAGRAPH/LINE_BREAK Attributes so we can
        // break up text chunks.
        AttributedString ret = asb.toAttributedString();

        // Note: The Working Group (in conjunction with XHTML working
        // group) has decided that multiple line elements collapse.
        int prevLN = 0;
        Iterator lnIter = lnLocs.iterator();
        while (lnIter.hasNext()) {
            int nextLN = ((Integer)lnIter.next()).intValue();
            if (nextLN == prevLN) continue;

            ret.addAttribute(FLOW_LINE_BREAK,
                             new Object(),
                             prevLN, nextLN);
            // System.out.println("Attr: [" + prevLN + "," + nextLN + "]");
            prevLN  = nextLN;
        }

        int start=0;
        int end;
        List emptyPara = null;
        for (int i=0; i<paraElems.size(); i++, start=end) {
            Element elem = (Element)paraElems.get(i);
            end  = ((Integer)paraEnds.get(i)).intValue();
            if (start == end) {
                if (emptyPara == null)
                    emptyPara = new LinkedList();
                emptyPara.add(makeMarginInfo(elem));
                continue;
            }
            // System.out.println("Para: [" + start + ", " + end + "]");
            ret.addAttribute(FLOW_PARAGRAPH, makeMarginInfo(elem), start, end);
            if (emptyPara != null) {
                ret.addAttribute(FLOW_EMPTY_PARAGRAPH, emptyPara, start, end);
                emptyPara = null;
            }
        }

        return ret;
    }

    protected List getRegions(BridgeContext ctx, Element element)  {
        List ret = new LinkedList();
        for (Node n = element.getFirstChild();
             n != null; n = n.getNextSibling()) {

            if (n.getNodeType()     != Node.ELEMENT_NODE) continue;
            if (!getNamespaceURI().equals(n.getNamespaceURI())) continue;

            Element e = (Element)n;

            String ln = e.getLocalName();
            if (!BATIK_EXT_FLOW_REGION_TAG.equals(ln))  continue;

            // our default alignment is to the top of the flow rect.
            float verticalAlignment = 0.0f;
            String verticalAlignmentAttribute
                = e.getAttribute(BATIK_EXT_VERTICAL_ALIGN_ATTRIBUTE);

            if ((verticalAlignmentAttribute != null) &&
                (verticalAlignmentAttribute.length() > 0)) {
                if (BATIK_EXT_ALIGN_TOP_VALUE.equals
                    (verticalAlignmentAttribute)) {
                    verticalAlignment = 0.0f;
                } else if (BATIK_EXT_ALIGN_MIDDLE_VALUE.equals
                           (verticalAlignmentAttribute)) {
                    verticalAlignment = 0.5f;
                } else if (BATIK_EXT_ALIGN_BOTTOM_VALUE.equals
                           (verticalAlignmentAttribute)) {
                    verticalAlignment = 1.0f;
                }
            }

            gatherRegionInfo(ctx, e, verticalAlignment, ret);
        }

        return ret;
    }

    protected void gatherRegionInfo(BridgeContext ctx, Element rgn,
                                    float verticalAlign, List regions) {

        for (Node n = rgn.getFirstChild();
             n != null; n = n.getNextSibling()) {

            if (n.getNodeType()     != Node.ELEMENT_NODE) continue;
            if (!getNamespaceURI().equals(n.getNamespaceURI())) continue;
            Element e = (Element)n;
            String ln = n.getLocalName();
            if (ln.equals(SVGConstants.SVG_RECT_TAG)) {
                UnitProcessor.Context uctx;
                uctx = UnitProcessor.createContext(ctx, e);

                RegionInfo ri = buildRegion(uctx, e, verticalAlign);
                if (ri != null)
                    regions.add(ri);
            }
        }
    }

    protected RegionInfo buildRegion(UnitProcessor.Context uctx,
                                     Element e,
                                     float verticalAlignment) {
        String s;

        // 'x' attribute - default is 0
        s = e.getAttribute(BATIK_EXT_X_ATTRIBUTE);
        float x = 0;
        if (s.length() != 0) {
            x = UnitProcessor.svgHorizontalCoordinateToUserSpace
                (s, BATIK_EXT_X_ATTRIBUTE, uctx);
        }

        // 'y' attribute - default is 0
        s = e.getAttribute(BATIK_EXT_Y_ATTRIBUTE);
        float y = 0;
        if (s.length() != 0) {
            y = UnitProcessor.svgVerticalCoordinateToUserSpace
                (s, BATIK_EXT_Y_ATTRIBUTE, uctx);
        }

        // 'width' attribute - required
        s = e.getAttribute(BATIK_EXT_WIDTH_ATTRIBUTE);
        float w;
        if (s.length() != 0) {
            w = UnitProcessor.svgHorizontalLengthToUserSpace
                (s, BATIK_EXT_WIDTH_ATTRIBUTE, uctx);
        } else {
            throw new BridgeException
                (ctx, e, ERR_ATTRIBUTE_MISSING,
                 new Object[] {BATIK_EXT_WIDTH_ATTRIBUTE, s});
        }
        // A value of zero disables rendering of the element
        if (w == 0) {
            return null;
        }

        // 'height' attribute - required
        s = e.getAttribute(BATIK_EXT_HEIGHT_ATTRIBUTE);
        float h;
        if (s.length() != 0) {
            h = UnitProcessor.svgVerticalLengthToUserSpace
                (s, BATIK_EXT_HEIGHT_ATTRIBUTE, uctx);
        } else {
            throw new BridgeException
                (ctx, e, ERR_ATTRIBUTE_MISSING,
                 new Object[] {BATIK_EXT_HEIGHT_ATTRIBUTE, s});
        }
        // A value of zero disables rendering of the element
        if (h == 0) {
            return null;
        }

        return new RegionInfo(x,y,w,h,verticalAlignment);
    }

    /**
     * Fills the given AttributedStringBuffer.
     */
    protected void fillAttributedStringBuffer(BridgeContext ctx,
                                              Element element,
                                              boolean top,
                                              Integer bidiLevel,
                                              Map initialAttributes,
                                              AttributedStringBuffer asb,
                                              List lnLocs) {
        // 'requiredFeatures', 'requiredExtensions', 'systemLanguage' &
        // 'display="none".
        if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) ||
            (!CSSUtilities.convertDisplay(element))) {
            return;
        }

        String  s        = XMLSupport.getXMLSpace(element);
        boolean preserve = s.equals(SVG_PRESERVE_VALUE);
        boolean prevEndsWithSpace;
        Element nodeElement = element;
        int elementStartChar = asb.length();

        if (top)
            endLimit = 0;
        if (preserve)
            endLimit = asb.length();

        Map map = initialAttributes == null
                ? new HashMap()
                : new HashMap(initialAttributes);
        initialAttributes = getAttributeMap(ctx, element, null, bidiLevel, map);
        Object o = map.get(TextAttribute.BIDI_EMBEDDING);
        Integer subBidiLevel = bidiLevel;
        if (o != null) {
            subBidiLevel = (Integer) o;
        }

        for (Node n = element.getFirstChild();
             n != null;
             n = n.getNextSibling()) {

            if (preserve) {
                prevEndsWithSpace = false;
            } else {
                if (asb.length() == 0) {
                    prevEndsWithSpace = true;
                } else {
                    prevEndsWithSpace = (asb.getLastChar() == ' ');
                }
            }

            switch (n.getNodeType()) {
            case Node.ELEMENT_NODE:
                // System.out.println("Element: " + n);
                if ((!getNamespaceURI().equals(n.getNamespaceURI())) &&
                    (!SVG_NAMESPACE_URI.equals(n.getNamespaceURI())))
                    break;

                nodeElement = (Element)n;

                String ln = n.getLocalName();

                if (ln.equals(BATIK_EXT_FLOW_LINE_TAG)) {
                    int before = asb.length();
                    fillAttributedStringBuffer(ctx, nodeElement, false,
                                               subBidiLevel, initialAttributes,
                                               asb, lnLocs);
                    // System.out.println("Line: " + asb.length() +
                    //                    " - '" +  asb + "'");
                    lnLocs.add(new Integer(asb.length()));
                    if (asb.length() != before) {
                        initialAttributes = null;
                    }
                } else if (ln.equals(BATIK_EXT_FLOW_SPAN_TAG) ||
                           ln.equals(SVG_ALT_GLYPH_TAG)) {
                    int before = asb.length();
                    fillAttributedStringBuffer(ctx, nodeElement, false,
                                               subBidiLevel, initialAttributes,
                                               asb, lnLocs);
                    if (asb.length() != before) {
                        initialAttributes = null;
                    }
                } else if (ln.equals(SVG_A_TAG)) {
                    if (ctx.isInteractive()) {
                        NodeEventTarget target = (NodeEventTarget)nodeElement;
                        UserAgent ua = ctx.getUserAgent();
                        SVGAElementBridge.CursorHolder ch;
                        ch = new SVGAElementBridge.CursorHolder
                            (CursorManager.DEFAULT_CURSOR);
                        target.addEventListenerNS
                            (XMLConstants.XML_EVENTS_NAMESPACE_URI,
                             SVG_EVENT_CLICK,
                             new SVGAElementBridge.AnchorListener(ua,ch),
                             false, null);

                        target.addEventListenerNS
                            (XMLConstants.XML_EVENTS_NAMESPACE_URI,
                             SVG_EVENT_MOUSEOVER,
                             new SVGAElementBridge.CursorMouseOverListener(ua,ch),
                             false, null);

                        target.addEventListenerNS
                            (XMLConstants.XML_EVENTS_NAMESPACE_URI,
                             SVG_EVENT_MOUSEOUT,
                             new SVGAElementBridge.CursorMouseOutListener(ua,ch),
                             false, null);
                    }
                    int before = asb.length();
                    fillAttributedStringBuffer(ctx, nodeElement, false,
                                               subBidiLevel, initialAttributes,
                                               asb, lnLocs);
                    if (asb.length() != before) {
                        initialAttributes = null;
                    }
                } else if (ln.equals(SVG_TREF_TAG)) {
                    String uriStr = XLinkSupport.getXLinkHref((Element)n);
                    Element ref = ctx.getReferencedElement((Element)n, uriStr);
                    s = TextUtilities.getElementContent(ref);
                    s = normalizeString(s, preserve, prevEndsWithSpace);
                    if (s.length() != 0) {
                        int trefStart = asb.length();
                        HashMap m = initialAttributes == null
                                ? new HashMap()
                                : new HashMap(initialAttributes);
                        getAttributeMap(ctx, nodeElement, null, bidiLevel, m);
                        asb.append(s, m);
                        int trefEnd = asb.length()-1;
                        TextPaintInfo tpi;
                        tpi = (TextPaintInfo)elemTPI.get(nodeElement);
                        tpi.startChar = trefStart;
                        tpi.endChar   = trefEnd;
                        initialAttributes = null;
                    }
                }
                break;

            case Node.TEXT_NODE:
            case Node.CDATA_SECTION_NODE:
                s = n.getNodeValue();
                s = normalizeString(s, preserve, prevEndsWithSpace);
                if (s.length() != 0) {
                    asb.append(s, map);
                    if (preserve) {
                        endLimit = asb.length();
                    }
                    initialAttributes = null;
                }
            }
        }

        if (top) {
            boolean strippedSome = false;
            while ((endLimit < asb.length()) && (asb.getLastChar() == ' ')) {
                asb.stripLast();
                strippedSome = true;
            }
            if (strippedSome) {
                Iterator iter = elemTPI.values().iterator();
                while (iter.hasNext()) {
                    TextPaintInfo tpi = (TextPaintInfo)iter.next();
                    if (tpi.endChar >= asb.length()) {
                        tpi.endChar = asb.length()-1;
                        if (tpi.startChar > tpi.endChar)
                            tpi.startChar = tpi.endChar;
                    }
                }
            }
        }
        int elementEndChar = asb.length()-1;
        TextPaintInfo tpi = (TextPaintInfo)elemTPI.get(element);
        tpi.startChar = elementStartChar;
        tpi.endChar   = elementEndChar;
    }

    protected Map getAttributeMap(BridgeContext ctx,
                                  Element element,
                                  TextPath textPath,
                                  Integer bidiLevel,
                                  Map result) {
        Map initialMap =
            super.getAttributeMap(ctx, element, textPath, bidiLevel, result);
        String s;
        s = element.getAttribute(BATIK_EXT_PREFORMATTED_ATTRIBUTE);
        if (s.length() != 0) {
            if (s.equals("true")) {
                result.put(PREFORMATTED, Boolean.TRUE);
            }
        }
        return initialMap;
    }

    protected void checkMap(Map attrs) {
        if (attrs.containsKey(TEXTPATH)) {
            return; // Problem, unsupported attr
        }

        if (attrs.containsKey(ANCHOR_TYPE)) {
            return; // Problem, unsupported attr
        }

        if (attrs.containsKey(LETTER_SPACING)) {
            return; // Problem, unsupported attr
        }

        if (attrs.containsKey(WORD_SPACING)) {
            return; // Problem, unsupported attr
        }

        if (attrs.containsKey(KERNING)) {
            return; // Problem, unsupported attr
        }
    }

    protected static final
    GVTAttributedCharacterIterator.TextAttribute TEXTPATH =
        GVTAttributedCharacterIterator.TextAttribute.TEXTPATH;

    protected static final
    GVTAttributedCharacterIterator.TextAttribute ANCHOR_TYPE =
        GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE;

    protected static final
    GVTAttributedCharacterIterator.TextAttribute LETTER_SPACING =
        GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING;

    protected static final
    GVTAttributedCharacterIterator.TextAttribute WORD_SPACING =
        GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING;

    protected static final
    GVTAttributedCharacterIterator.TextAttribute KERNING =
        GVTAttributedCharacterIterator.TextAttribute.KERNING;

    public static class LineBreakInfo {
        int     breakIdx;
        float   lineAdvAdj;
        boolean relative;
        /**
         * @param breakIdx   The character after which to break.
         * @param lineAdvAdj The line advance adjustment.
         * @param relative   If true lineAdvAdj must be multiplied by
         *                   the line height.
         */
        public LineBreakInfo(int breakIdx, float lineAdvAdj, boolean relative){
            this.breakIdx = breakIdx;
            this.lineAdvAdj = lineAdvAdj;
            this.relative = relative;
        }
        public int     getBreakIdx()   { return breakIdx; }
        public boolean isRelative()    { return relative; }
        public float   getLineAdvAdj() { return lineAdvAdj; }
    }

    public MarginInfo makeMarginInfo(Element e) {
        String s;
        float top=0, right=0, bottom=0, left=0;

        s = e.getAttribute(BATIK_EXT_MARGIN_ATTRIBUTE);
        try {
            if (s.length() != 0) {
                float f = Float.parseFloat(s);
                top=right=bottom=left=f;
            }
        } catch(NumberFormatException nfe) { /* nothing */ }

        s = e.getAttribute(BATIK_EXT_TOP_MARGIN_ATTRIBUTE);
        try {
            if (s.length() != 0) {
                float f = Float.parseFloat(s);
                top = f;
            }
        } catch(NumberFormatException nfe) { /* nothing */ }
        s = e.getAttribute(BATIK_EXT_RIGHT_MARGIN_ATTRIBUTE);
        try {
            if (s.length() != 0) {
                float f = Float.parseFloat(s);
                right = f;
            }
        } catch(NumberFormatException nfe) { /* nothing */ }
        s = e.getAttribute(BATIK_EXT_BOTTOM_MARGIN_ATTRIBUTE);
        try {
            if (s.length() != 0) {
                float f = Float.parseFloat(s);
                bottom = f;
            }
        } catch(NumberFormatException nfe) { /* nothing */ }
        s = e.getAttribute(BATIK_EXT_LEFT_MARGIN_ATTRIBUTE);
        try {
            if (s.length() != 0) {
                float f = Float.parseFloat(s);
                left = f;
            }
        } catch(NumberFormatException nfe) { /* nothing */ }

        float indent = 0;
        s = e.getAttribute(BATIK_EXT_INDENT_ATTRIBUTE);
        try {
            if (s.length() != 0) {
                float f = Float.parseFloat(s);
                indent = f;
            }
        } catch(NumberFormatException nfe) { /* nothing */ }

        int justification = MarginInfo.JUSTIFY_START;
        s = e.getAttribute(BATIK_EXT_JUSTIFICATION_ATTRIBUTE);
        try {
            if (s.length() != 0) {
                if (BATIK_EXT_JUSTIFICATION_START_VALUE.equals(s)) {
                    justification = MarginInfo.JUSTIFY_START;
                } else if (BATIK_EXT_JUSTIFICATION_MIDDLE_VALUE.equals(s)) {
                    justification = MarginInfo.JUSTIFY_MIDDLE;
                } else if (BATIK_EXT_JUSTIFICATION_END_VALUE.equals(s)) {
                    justification = MarginInfo.JUSTIFY_END;
                } else if (BATIK_EXT_JUSTIFICATION_FULL_VALUE.equals(s)) {
                    justification = MarginInfo.JUSTIFY_FULL;
                }
            }
        } catch(NumberFormatException nfe) { /* nothing */ }

        String ln = e.getLocalName();
        boolean rgnBr = ln.equals(BATIK_EXT_FLOW_REGION_BREAK_TAG);
        return new MarginInfo(top, right, bottom, left,
                              indent, justification, rgnBr);
    }

    /**
     * Bridge class for flow text children that contain text.
     */
    protected class BatikFlowContentBridge
        extends AbstractTextChildTextContent {

        /**
         * Creates a new FlowContentBridge.
         */
        public BatikFlowContentBridge(BridgeContext ctx,
                                 SVGTextElementBridge parent,
                                 Element e) {
            super(ctx, parent, e);
        }
    }

}