text:
#region --- License ---

#endregion

#if !MONOGAME && !OPENTK && !SHARPDX && !XNA && !UNITY3D

using System;
using System.Linq;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace SharpNav.Geometry
{
	///
	/// Represents a 3D vector using three single-precision floating-point numbers.
	///
	/// <remarks>
	/// The Vector3 structure is suitable for interoperation with unmanaged code requiring three consecutive floats.
	/// </remarks>
	[Serializable]
	[StructLayout(LayoutKind.Sequential)]
	public struct Vector3 : IEquatable<Vector3>
	{
		#region Fields

		///
		/// The X component of the Vector3.
		///
		public float X;

		///
		/// The Y component of the Vector3.
		///
		public float Y;

		///
		/// The Z component of the Vector3.
		///
		public float Z;

		#endregion

		#region Constructors

		///
		/// Constructs a new instance.
		///
		/// <param name="value">The value that will initialize this instance.</param>
		public Vector3(float value)
		{
			X = value;
			Y = value;
			Z = value;
		}

		///
		/// Constructs a new Vector3.
		///
		/// <param name="x">The x component of the Vector3.</param>
		/// <param name="y">The y component of the Vector3.</param>
		/// <param name="z">The z component of the Vector3.</param>
		public Vector3(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		///
		/// Constructs a new Vector3 from the given Vector3.
		///
		/// <param name="v">The Vector3 to copy components from.</param>
		public Vector3(Vector3 v)
		{
			X = v.X;
			Y = v.Y;
			Z = v.Z;
		}

		#endregion

		#region Public Members

		///
		/// Gets or sets the value at the index of the Vector.
		///
		public float this[int index]
		{
			get
			{
				switch (index)
				{
					case 0:
						return X;
					case 1:
						return Y;
					case 2:
						return Z;
					default:
						throw new ArgumentOutOfRangeException("You tried to access this vector at index: " + index);
				}
			}

			set
			{
				switch (index)
				{
					case 0:
						X = value;
						break;
					case 1:
						Y = value;
						break;
					case 2:
						Z = value;
						break;
					default:
						throw new ArgumentOutOfRangeException("You tried to set this vector at index: " + index);
				}
			}
		}

		#region Instance

		#region public void Add()

		/// Add the Vector passed as parameter to this instance.
		/// <param name="right">Right operand. This parameter is only read from.</param>
		[CLSCompliant(false)]
		[Obsolete("Use static Add() method instead.")]
		public void Add(Vector3 right)
		{
			this.X += right.X;
			this.Y += right.Y;
			this.Z += right.Z;
		}

		/// Add the Vector passed as parameter to this instance.
		/// <param name="right">Right operand. This parameter is only read from.</param>
		[CLSCompliant(false)]
		[Obsolete("Use static Add() method instead.")]
		public void Add(ref Vector3 right)
		{
			this.X += right.X;
			this.Y += right.Y;
			this.Z += right.Z;
		}

		#endregion public void Add()

		#region public void Sub()

		/// Subtract the Vector passed as parameter from this instance.
		/// <param name="right">Right operand. This parameter is only read from.</param>
		[CLSCompliant(false)]
		[Obsolete("Use static Subtract() method instead.")]
		public void Sub(Vector3 right)
		{
			this.X -= right.X;
			this.Y -= right.Y;
			this.Z -= right.Z;
		}

		/// Subtract the Vector passed as parameter from this instance.
		/// <param name="right">Right operand. This parameter is only read from.</param>
		[CLSCompliant(false)]
		[Obsolete("Use static Subtract() method instead.")]
		public void Sub(ref Vector3 right)
		{
			this.X -= right.X;
			this.Y -= right.Y;
			this.Z -= right.Z;
		}

		#endregion public void Sub()

		#region public void Mult()

		/// Multiply this instance by a scalar.
		/// <param name="f">Scalar operand.</param>
		[Obsolete("Use static Multiply() method instead.")]
		public void Mult(float f)
		{
			this.X *= f;
			this.Y *= f;
			this.Z *= f;
		}

		#endregion public void Mult()

		#region public void Div()

		/// Divide this instance by a scalar.
		/// <param name="f">Scalar operand.</param>
		[Obsolete("Use static Divide() method instead.")]
		public void Div(float f)
		{
			float mult = 1.0f / f;
			this.X *= mult;
			this.Y *= mult;
			this.Z *= mult;
		}

		#endregion public void Div()

		#region public float Length

		///
		/// Gets the length (magnitude) of the vector.
		///
		/// <see cref="LengthSquared"/>
		public float Length()
		{
			return (float)System.Math.Sqrt(X * X + Y * Y + Z * Z);
		}

		#endregion

		#region public float LengthSquared

		///
		/// Gets the square of the vector length (magnitude).
		///
		/// <remarks>
		/// This property avoids the costly square root operation required by the Length property. This makes it more suitable
		/// for comparisons.
		/// </remarks>
		/// <see cref="Length"/>
		public float LengthSquared()
		{
			return X * X + Y * Y + Z * Z;
		}

		#endregion

		///
		/// Returns a copy of the Vector3 scaled to unit length.
		///
		public Vector3 Normalized()
		{
			Vector3 v = this;
			v.Normalize();
			return v;
		}

		#region public void Normalize()

		///
		/// Scales the Vector3 to unit length.
		///
		public void Normalize()
		{
			float scale = 1.0f / this.Length();
			X *= scale;
			Y *= scale;
			Z *= scale;
		}

		#endregion

		#region public void Scale()

		///
		/// Scales the current Vector3 by the given amounts.
		///
		/// <param name="sx">The scale of the X component.</param>
		/// <param name="sy">The scale of the Y component.</param>
		/// <param name="sz">The scale of the Z component.</param>
		[Obsolete("Use static Multiply() method instead.")]
		public void Scale(float sx, float sy, float sz)
		{
			this.X = X * sx;
			this.Y = Y * sy;
			this.Z = Z * sz;
		}

		/// Scales this instance by the given parameter.
		/// <param name="scale">The scaling of the individual components.</param>
		[CLSCompliant(false)]
		[Obsolete("Use static Multiply() method instead.")]
		public void Scale(Vector3 scale)
		{
			this.X *= scale.X;
			this.Y *= scale.Y;
			this.Z *= scale.Z;
		}

		/// Scales this instance by the given parameter.
		/// <param name="scale">The scaling of the individual components.</param>
		[CLSCompliant(false)]
		[Obsolete("Use static Multiply() method instead.")]
		public void Scale(ref Vector3 scale)
		{
			this.X *= scale.X;
			this.Y *= scale.Y;
			this.Z *= scale.Z;
		}

		#endregion public void Scale()

		#endregion

		#region Static

		#region Fields

		///
		/// Defines a unit-length Vector3 that points towards the X-axis.
		///
		public static readonly Vector3 UnitX = new Vector3(1, 0, 0);

		///
		/// Defines a unit-length Vector3 that points towards the Y-axis.
		///
		public static readonly Vector3 UnitY = new Vector3(0, 1, 0);

		///
		/// /// Defines a unit-length Vector3 that points towards the Z-axis.
		///
		public static readonly Vector3 UnitZ = new Vector3(0, 0, 1);

		///
		/// Defines a zero-length Vector3.
		///
		public static readonly Vector3 Zero = new Vector3(0, 0, 0);

		///
		/// Defines an instance with all components set to 1.
		///
		public static readonly Vector3 One = new Vector3(1, 1, 1);

		///
		/// Defines the size of the Vector3 struct in bytes.
		///
		public static readonly int SizeInBytes = Marshal.SizeOf(new Vector3());

		#endregion

		#region Obsolete

		#region Sub

		///
		/// Subtract one Vector from another
		///
		/// <param name="a">First operand</param>
		/// <param name="b">Second operand</param>
		/// <returns>Result of subtraction</returns>
		[Obsolete("Use static Subtract() method instead.")]
		public static Vector3 Sub(Vector3 a, Vector3 b)
		{
			a.X -= b.X;
			a.Y -= b.Y;
			a.Z -= b.Z;
			return a;
		}

		///
		/// Subtract one Vector from another
		///
		/// <param name="a">First operand</param>
		/// <param name="b">Second operand</param>
		/// <param name="result">Result of subtraction</param>
		[Obsolete("Use static Subtract() method instead.")]
		public static void Sub(ref Vector3 a, ref Vector3 b, out Vector3 result)
		{
			result.X = a.X - b.X;
			result.Y = a.Y - b.Y;
			result.Z = a.Z - b.Z;
		}

		#endregion

		#region Mult

		///
		/// Multiply a vector and a scalar
		///
		/// <param name="a">Vector operand</param>
		/// <param name="f">Scalar operand</param>
		/// <returns>Result of the multiplication</returns>
		[Obsolete("Use static Multiply() method instead.")]
		public static Vector3 Mult(Vector3 a, float f)
		{
			a.X *= f;
			a.Y *= f;
			a.Z *= f;
			return a;
		}

		///
		/// Multiply a vector and a scalar
		///
		/// <param name="a">Vector operand</param>
		/// <param name="f">Scalar operand</param>
		/// <param name="result">Result of the multiplication</param>
		[Obsolete("Use static Multiply() method instead.")]
		public static void Mult(ref Vector3 a, float f, out Vector3 result)
		{
			result.X = a.X * f;
			result.Y = a.Y * f;
			result.Z = a.Z * f;
		}

		#endregion

		#region Div

		///
		/// Divide a vector by a scalar
		///
		/// <param name="a">Vector operand</param>
		/// <param name="f">Scalar operand</param>
		/// <returns>Result of the division</returns>
		[Obsolete("Use static Divide() method instead.")]
		public static Vector3 Div(Vector3 a, float f)
		{
			float mult = 1.0f / f;
			a.X *= mult;
			a.Y *= mult;
			a.Z *= mult;
			return a;
		}

		///
		/// Divide a vector by a scalar
		///
		/// <param name="a">Vector operand</param>
		/// <param name="f">Scalar operand</param>
		/// <param name="result">Result of the division</param>
		[Obsolete("Use static Divide() method instead.")]
		public static void Div(ref Vector3 a, float f, out Vector3 result)
		{
			float mult = 1.0f / f;
			result.X = a.X * mult;
			result.Y = a.Y * mult;
			result.Z = a.Z * mult;
		}

		#endregion

		#endregion

		#region Add

		///
		/// Adds two vectors.
		///
		/// <param name="a">Left operand.</param>
		/// <param name="b">Right operand.</param>
		/// <returns>Result of operation.</returns>
		public static Vector3 Add(Vector3 a, Vector3 b)
		{
			Add(ref a, ref b, out a);
			return a;
		}

		///
		/// Adds two vectors.
		///
		/// <param name="a">Left operand.</param>
		/// <param name="b">Right operand.</param>
		/// <param name="result">Result of operation.</param>
		public static void Add(ref Vector3 a, ref Vector3 b, out Vector3 result)
		{
			result = new Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
		}

		#endregion

		#region Subtract

		///
		/// Subtract one Vector from another
		///
		/// <param name="a">First operand</param>
		/// <param name="b">Second operand</param>
		/// <returns>Result of subtraction</returns>
		public static Vector3 Subtract(Vector3 a, Vector3 b)
		{
			Subtract(ref a, ref b, out a);
			return a;
		}

		///
		/// Subtract one Vector from another
		///
		/// <param name="a">First operand</param>
		/// <param name="b">Second operand</param>
		/// <param name="result">Result of subtraction</param>
		public static void Subtract(ref Vector3 a, ref Vector3 b, out Vector3 result)
		{
			result = new Vector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
		}

		#endregion

		#region Multiply

		///
		/// Multiplies a vector by a scalar.
		///
		/// <param name="vector">Left operand.</param>
		/// <param name="scale">Right operand.</param>
		/// <returns>Result of the operation.</returns>
		public static Vector3 Multiply(Vector3 vector, float scale)
		{
			Multiply(ref vector, scale, out vector);
			return vector;
		}

		///
		/// Multiplies a vector by a scalar.
		///
		/// <param name="vector">Left operand.</param>
		/// <param name="scale">Right operand.</param>
		/// <param name="result">Result of the operation.</param>
		public static void Multiply(ref Vector3 vector, float scale, out Vector3 result)
		{
			result = new Vector3(vector.X * scale, vector.Y * scale, vector.Z * scale);
		}

		///
		/// Multiplies a vector by the components a vector (scale).
		///
		/// <param name="vector">Left operand.</param>
		/// <param name="scale">Right operand.</param>
		/// <returns>Result of the operation.</returns>
		public static Vector3 Multiply(Vector3 vector, Vector3 scale)
		{
			Multiply(ref vector, ref scale, out vector);
			return vector;
		}

		///
		/// Multiplies a vector by the components of a vector (scale).
		///
		/// <param name="vector">Left operand.</param>
		/// <param name="scale">Right operand.</param>
		/// <param name="result">Result of the operation.</param>
		public static void Multiply(ref Vector3 vector, ref Vector3 scale, out Vector3 result)
		{
			result = new Vector3(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z);
		}

		#endregion

		#region Divide

		///
		/// Divides a vector by a scalar.
		///
		/// <param name="vector">Left operand.</param>
		/// <param name="scale">Right operand.</param>
		/// <returns>Result of the operation.</returns>
		public static Vector3 Divide(Vector3 vector, float scale)
		{
			Divide(ref vector, scale, out vector);
			return vector;
		}

		///
		/// Divides a vector by a scalar.
		///
		/// <param name="vector">Left operand.</param>
		/// <param name="scale">Right operand.</param>
		/// <param name="result">Result of the operation.</param>
		public static void Divide(ref Vector3 vector, float scale, out Vector3 result)
		{
			Multiply(ref vector, 1 / scale, out result);
		}

		///
		/// Divides a vector by the components of a vector (scale).
		///
		/// <param name="vector">Left operand.</param>
		/// <param name="scale">Right operand.</param>
		/// <returns>Result of the operation.</returns>
		public static Vector3 Divide(Vector3 vector, Vector3 scale)
		{
			Divide(ref vector, ref scale, out vector);
			return vector;
		}

		///
		/// Divide a vector by the components of a vector (scale).
		///
		/// <param name="vector">Left operand.</param>
		/// <param name="scale">Right operand.</param>
		/// <param name="result">Result of the operation.</param>
		public static void Divide(ref Vector3 vector, ref Vector3 scale, out Vector3 result)
		{
			result = new Vector3(vector.X / scale.X, vector.Y / scale.Y, vector.Z / scale.Z);
		}

		#endregion

		#region ComponentMin

		///
		/// Calculate the component-wise minimum of two vectors
		///
		/// <param name="a">First operand</param>
		/// <param name="b">Second operand</param>
		/// <returns>The component-wise minimum</returns>
		public static Vector3 ComponentMin(Vector3 a, Vector3 b)
		{
			a.X = a.X < b.X ? a.X : b.X;
			a.Y = a.Y < b.Y ? a.Y : b.Y;
			a.Z = a.Z < b.Z ? a.Z : b.Z;
			return a;
		}

		///
		/// Calculate the component-wise minimum of two vectors
		///
		/// <param name="a">First operand</param>
		/// <param name="b">Second operand</param>
		/// <param name="result">The component-wise minimum</param>
		public static void ComponentMin(ref Vector3 a, ref Vector3 b, out Vector3 result)
		{
			result.X = a.X < b.X ? a.X : b.X;
			result.Y = a.Y < b.Y ? a.Y : b.Y;
			result.Z = a.Z < b.Z ? a.Z : b.Z;
		}

		#endregion

		#region ComponentMax

		///
		/// Calculate the component-wise maximum of two vectors
		///
		/// <param name="a">First operand</param>
		/// <param name="b">Second operand</param>
		/// <returns>The component-wise maximum</returns>
		public static Vector3 ComponentMax(Vector3 a, Vector3 b)
		{
			a.X = a.X > b.X ? a.X : b.X;
			a.Y = a.Y > b.Y ? a.Y : b.Y;
			a.Z = a.Z > b.Z ? a.Z : b.Z;
			return a;
		}

		///
		/// Calculate the component-wise maximum of two vectors
		///
		/// <param name="a">First operand</param>
		/// <param name="b">Second operand</param>
		/// <param name="result">The component-wise maximum</param>
		public static void ComponentMax(ref Vector3 a, ref Vector3 b, out Vector3 result)
		{
			result.X = a.X > b.X ? a.X : b.X;
			result.Y = a.Y > b.Y ? a.Y : b.Y;
			result.Z = a.Z > b.Z ? a.Z : b.Z;
		}

		#endregion

		#region Min

		///
		/// Returns the Vector3 with the minimum magnitude
		///
		/// <param name="left">Left operand</param>
		/// <param name="right">Right operand</param>
		/// <returns>The minimum Vector3</returns>
		public static Vector3 Min(Vector3 left, Vector3 right)
		{
			return left.LengthSquared() < right.LengthSquared() ? left : right;
		}

		#endregion

		#region Max

		///
		/// Returns the Vector3 with the minimum magnitude
		///
		/// <param name="left">Left operand</param>
		/// <param name="right">Right operand</param>
		/// <returns>The minimum Vector3</returns>
		public static Vector3 Max(Vector3 left, Vector3 right)
		{
			return left.LengthSquared() >= right.LengthSquared() ? left : right;
		}

		#endregion

		#region Clamp

		///
		/// Clamp a vector to the given minimum and maximum vectors
		///
		/// <param name="vec">Input vector</param>
		/// <param name="min">Minimum vector</param>
		/// <param name="max">Maximum vector</param>
		/// <returns>The clamped vector</returns>
		public static Vector3 Clamp(Vector3 vec, Vector3 min, Vector3 max)
		{
			vec.X = vec.X < min.X ? min.X : vec.X > max.X ? max.X : vec.X;
			vec.Y = vec.Y < min.Y ? min.Y : vec.Y > max.Y ? max.Y : vec.Y;
			vec.Z = vec.Z < min.Z ? min.Z : vec.Z > max.Z ? max.Z : vec.Z;
			return vec;
		}

		///
		/// Clamp a vector to the given minimum and maximum vectors
		///
		/// <param name="vec">Input vector</param>
		/// <param name="min">Minimum vector</param>
		/// <param name="max">Maximum vector</param>
		/// <param name="result">The clamped vector</param>
		public static void Clamp(ref Vector3 vec, ref Vector3 min, ref Vector3 max, out Vector3 result)
		{
			result.X = vec.X < min.X ? min.X : vec.X > max.X ? max.X : vec.X;
			result.Y = vec.Y < min.Y ? min.Y : vec.Y > max.Y ? max.Y : vec.Y;
			result.Z = vec.Z < min.Z ? min.Z : vec.Z > max.Z ? max.Z : vec.Z;
		}

		#endregion

		#region Normalize

		///
		/// Scale a vector to unit length
		///
		/// <param name="vec">The input vector</param>
		/// <returns>The normalized vector</returns>
		public static Vector3 Normalize(Vector3 vec)
		{
			float scale = 1.0f / vec.Length();
			vec.X *= scale;
			vec.Y *= scale;
			vec.Z *= scale;
			return vec;
		}

		///
		/// Scale a vector to unit length
		///
		/// <param name="vec">The input vector</param>
		/// <param name="result">The normalized vector</param>
		public static void Normalize(ref Vector3 vec, out Vector3 result)
		{
			float scale = 1.0f / vec.Length();
			result.X = vec.X * scale;
			result.Y = vec.Y * scale;
			result.Z = vec.Z * scale;
		}

		#endregion

		#region Dot

		///
		/// Calculate the dot (scalar) product of two vectors
		///
		/// <param name="left">First operand</param>
		/// <param name="right">Second operand</param>
		/// <returns>The dot product of the two inputs</returns>
		public static float Dot(Vector3 left, Vector3 right)
		{
			return left.X * right.X + left.Y * right.Y + left.Z * right.Z;
		}

		///
		/// Calculate the dot (scalar) product of two vectors
		///
		/// <param name="left">First operand</param>
		/// <param name="right">Second operand</param>
		/// <param name="result">The dot product of the two inputs</param>
		public static void Dot(ref Vector3 left, ref Vector3 right, out float result)
		{
			result = left.X * right.X + left.Y * right.Y + left.Z * right.Z;
		}

		#endregion

		#region Cross

		///
		/// Caclulate the cross (vector) product of two vectors
		///
		/// <param name="left">First operand</param>
		/// <param name="right">Second operand</param>
		/// <returns>The cross product of the two inputs</returns>
		public static Vector3 Cross(Vector3 left, Vector3 right)
		{
			Vector3 result;
			Cross(ref left, ref right, out result);
			return result;
		}

		///
		/// Caclulate the cross (vector) product of two vectors
		///
		/// <param name="left">First operand</param>
		/// <param name="right">Second operand</param>
		/// <returns>The cross product of the two inputs</returns>
		/// <param name="result">The cross product of the two inputs</param>
		public static void Cross(ref Vector3 left, ref Vector3 right, out Vector3 result)
		{
			result = new Vector3(left.Y * right.Z - left.Z * right.Y,
				left.Z * right.X - left.X * right.Z,
				left.X * right.Y - left.Y * right.X);
		}

		#endregion

		#region Lerp

		///
		/// Returns a new Vector that is the linear blend of the 2 given Vectors
		///
		/// <param name="a">First input vector</param>
		/// <param name="b">Second input vector</param>
		/// <param name="blend">The blend factor. a when blend=0, b when blend=1.</param>
		/// <returns>a when blend=0, b when blend=1, and a linear combination otherwise</returns>
		public static Vector3 Lerp(Vector3 a, Vector3 b, float blend)
		{
			a.X = blend * (b.X - a.X) + a.X;
			a.Y = blend * (b.Y - a.Y) + a.Y;
			a.Z = blend * (b.Z - a.Z) + a.Z;
			return a;
		}

		///
		/// Returns a new Vector that is the linear blend of the 2 given Vectors
		///
		/// <param name="a">First input vector</param>
		/// <param name="b">Second input vector</param>
		/// <param name="blend">The blend factor. a when blend=0, b when blend=1.</param>
		/// <param name="result">a when blend=0, b when blend=1, and a linear combination otherwise</param>
		public static void Lerp(ref Vector3 a, ref Vector3 b, float blend, out Vector3 result)
		{
			result.X = blend * (b.X - a.X) + a.X;
			result.Y = blend * (b.Y - a.Y) + a.Y;
			result.Z = blend * (b.Z - a.Z) + a.Z;
		}

		#endregion

		#region Barycentric

		///
		/// Interpolate 3 Vectors using Barycentric coordinates
		///
		/// <param name="a">First input Vector</param>
		/// <param name="b">Second input Vector</param>
		/// <param name="c">Third input Vector</param>
		/// <param name="u">First Barycentric Coordinate</param>
		/// <param name="v">Second Barycentric Coordinate</param>
		/// <returns>a when u=v=0, b when u=1,v=0, c when u=0,v=1, and a linear combination of a,b,c otherwise</returns>
		public static Vector3 BaryCentric(Vector3 a, Vector3 b, Vector3 c, float u, float v)
		{
			return a + u * (b - a) + v * (c - a);
		}

		/// Interpolate 3 Vectors using Barycentric coordinates
		/// <param name="a">First input Vector.</param>
		/// <param name="b">Second input Vector.</param>
		/// <param name="c">Third input Vector.</param>
		/// <param name="u">First Barycentric Coordinate.</param>
		/// <param name="v">Second Barycentric Coordinate.</param>
		/// <param name="result">Output Vector. a when u=v=0, b when u=1,v=0, c when u=0,v=1, and a linear combination of a,b,c otherwise</param>
		public static void BaryCentric(ref Vector3 a, ref Vector3 b, ref Vector3 c, float u, float v, out Vector3 result)
		{
			result = a; // copy

			Vector3 temp = b; // copy
			Subtract(ref temp, ref a, out temp);
			Multiply(ref temp, u, out temp);
			Add(ref result, ref temp, out result);

			temp = c; // copy
			Subtract(ref temp, ref a, out temp);
			Multiply(ref temp, v, out temp);
			Add(ref result, ref temp, out result);
		}

		#endregion

		#region CalculateAngle

		///
		/// Calculates the angle (in radians) between two vectors.
		///
		/// <param name="first">The first vector.</param>
		/// <param name="second">The second vector.</param>
		/// <returns>Angle (in radians) between the vectors.</returns>
		/// <remarks>Note that the returned angle is never bigger than the constant Pi.</remarks>
		public static float CalculateAngle(Vector3 first, Vector3 second)
		{
			return (float)System.Math.Acos((Vector3.Dot(first, second)) / (first.Length() * second.Length()));
		}

		/// Calculates the angle (in radians) between two vectors.
		/// <param name="first">The first vector.</param>
		/// <param name="second">The second vector.</param>
		/// <param name="result">Angle (in radians) between the vectors.</param>
		/// <remarks>Note that the returned angle is never bigger than the constant Pi.</remarks>
		public static void CalculateAngle(ref Vector3 first, ref Vector3 second, out float result)
		{
			float temp;
			Vector3.Dot(ref first, ref second, out temp);
			result = (float)System.Math.Acos(temp / (first.Length() * second.Length()));
		}

		#endregion

		#endregion

		#region Swizzle

		#region 3-component

		///
		/// Gets or sets an OpenTK.Vector3 with the X, Z, and Y components of this instance.
		///
		[JsonIgnore]
		public Vector3 Xzy { get { return new Vector3(X, Z, Y); } set { X = value.X; Z = value.Y; Y = value.Z; } }

		///
		/// Gets or sets an OpenTK.Vector3 with the Y, X, and Z components of this instance.
		///
		[JsonIgnore]
		public Vector3 Yxz { get { return new Vector3(Y, X, Z); } set { Y = value.X; X = value.Y; Z = value.Z; } }

		///
		/// Gets or sets an OpenTK.Vector3 with the Y, Z, and X components of this instance.
		///
		[JsonIgnore]
		public Vector3 Yzx { get { return new Vector3(Y, Z, X); } set { Y = value.X; Z = value.Y; X = value.Z; } }

		///
		/// Gets or sets an OpenTK.Vector3 with the Z, X, and Y components of this instance.
		///
		[JsonIgnore]
		public Vector3 Zxy { get { return new Vector3(Z, X, Y); } set { Z = value.X; X = value.Y; Y = value.Z; } }

		///
		/// Gets or sets an OpenTK.Vector3 with the Z, Y, and X components of this instance.
		///
		[JsonIgnore]
		public Vector3 Zyx { get { return new Vector3(Z, Y, X); } set { Z = value.X; Y = value.Y; X = value.Z; } }

		#endregion

		#endregion

		#region Operators

		///
		/// Adds two instances.
		///
		/// <param name="left">The first instance.</param>
		/// <param name="right">The second instance.</param>
		/// <returns>The result of the calculation.</returns>
		public static Vector3 operator +(Vector3 left, Vector3 right)
		{
			left.X += right.X;
			left.Y += right.Y;
			left.Z += right.Z;
			return left;
		}

		///
		/// Subtracts two instances.
		///
		/// <param name="left">The first instance.</param>
		/// <param name="right">The second instance.</param>
		/// <returns>The result of the calculation.</returns>
		public static Vector3 operator -(Vector3 left, Vector3 right)
		{
			left.X -= right.X;
			left.Y -= right.Y;
			left.Z -= right.Z;
			return left;
		}

		///
		/// Negates an instance.
		///
		/// <param name="vec">The instance.</param>
		/// <returns>The result of the calculation.</returns>
		public static Vector3 operator -(Vector3 vec)
		{
			vec.X = -vec.X;
			vec.Y = -vec.Y;
			vec.Z = -vec.Z;
			return vec;
		}

		///
		/// Multiplies an instance by a scalar.
		///
		/// <param name="vec">The instance.</param>
		/// <param name="scale">The scalar.</param>
		/// <returns>The result of the calculation.</returns>
		public static Vector3 operator *(Vector3 vec, float scale)
		{
			vec.X *= scale;
			vec.Y *= scale;
			vec.Z *= scale;
			return vec;
		}

		///
		/// Multiplies an instance by a scalar.
		///
		/// <param name="scale">The scalar.</param>
		/// <param name="vec">The instance.</param>
		/// <returns>The result of the calculation.</returns>
		public static Vector3 operator *(float scale, Vector3 vec)
		{
			vec.X *= scale;
			vec.Y *= scale;
			vec.Z *= scale;
			return vec;
		}

		///
		/// Component-wise multiplication between the specified instance by a scale vector.
		///
		/// <param name="scale">Left operand.</param>
		/// <param name="vec">Right operand.</param>
		/// <returns>Result of multiplication.</returns>
		public static Vector3 operator *(Vector3 vec, Vector3 scale)
		{
			vec.X *= scale.X;
			vec.Y *= scale.Y;
			vec.Z *= scale.Z;
			return vec;
		}

		///
		/// Divides an instance by a scalar.
		///
		/// <param name="vec">The instance.</param>
		/// <param name="scale">The scalar.</param>
		/// <returns>The result of the calculation.</returns>
		public static Vector3 operator /(Vector3 vec, float scale)
		{
			float mult = 1.0f / scale;
			vec.X *= mult;
			vec.Y *= mult;
			vec.Z *= mult;
			return vec;
		}

		///
		/// Compares two instances for equality.
		///
		/// <param name="left">The first instance.</param>
		/// <param name="right">The second instance.</param>
		/// <returns>True, if left equals right; false otherwise.</returns>
		public static bool operator ==(Vector3 left, Vector3 right)
		{
			return left.Equals(right);
		}

		///
		/// Compares two instances for inequality.
		///
		/// <param name="left">The first instance.</param>
		/// <param name="right">The second instance.</param>
		/// <returns>True, if left does not equa lright; false otherwise.</returns>
		public static bool operator !=(Vector3 left, Vector3 right)
		{
			return !left.Equals(right);
		}

		#endregion

		#region Overrides

		#region public override string ToString()

		private static string listSeparator = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator;
		///
		/// Returns a System.String that represents the current Vector3.
		///
		/// <returns></returns>
		public override string ToString()
		{
			return String.Format("({0}{3} {1}{3} {2})", X, Y, Z, listSeparator);
		}

		#endregion

		#region public override int GetHashCode()

		///
		/// Returns the hashcode for this instance.
		///
		/// <returns>A System.Int32 containing the unique hashcode for this instance.</returns>
		public override int GetHashCode()
		{
			return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
		}

		#endregion

		#region public override bool Equals(object obj)

		///
		/// Indicates whether this instance and a specified object are equal.
		///
		/// <param name="obj">The object to compare to.</param>
		/// <returns>True if the instances are equal; false otherwise.</returns>
		public override bool Equals(object obj)
		{
			if (!(obj is Vector3))
				return false;

			return this.Equals((Vector3)obj);
		}

		#endregion

		#endregion

		#endregion

		#region IEquatable<Vector3> Members

		/// Indicates whether the current vector is equal to another vector.
		/// <param name="other">A vector to compare with this vector.</param>
		/// <returns>true if the current vector is equal to the vector parameter; otherwise, false.</returns>
		public bool Equals(Vector3 other)
		{
			return
				X == other.X &&
				Y == other.Y &&
				Z == other.Z;
		}

		#endregion
	}
}

#endif


text:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using Microsoft.Win32;
using WMI;
using ServiceType = WMI.ServiceType;
using System.Reflection;

namespace winsw
{
    public class WrapperService : ServiceBase, EventLogger
    {
        private SERVICE_STATUS _wrapperServiceStatus;

        private readonly Process _process = new Process();
        private readonly ServiceDescriptor _descriptor;
        private Dictionary<string, string> _envs;

        /// <summary>
        /// Indicates to the watch dog thread that we are going to terminate the process,
        /// so don't try to kill us when the child exits.
        /// </summary>
        private bool _orderlyShutdown;
        private bool _systemShuttingdown;

        /// <summary>
        /// Version of Windows service wrapper
        /// </summary>
        /// <remarks>
        /// The version will be taken from <see cref="AssemblyInfo"/>
        /// </remarks>
        public static Version Version
        {
            get { return Assembly.GetExecutingAssembly().GetName().Version; }
        }

        public WrapperService(ServiceDescriptor descriptor)
        {
            _descriptor = descriptor;
            ServiceName = _descriptor.Id;
            CanShutdown = true;
            CanStop = true;
            CanPauseAndContinue = false;
            AutoLog = true;
            _systemShuttingdown = false;
        }

        public WrapperService() : this (new ServiceDescriptor())
        {
        }

        /// <summary>
        /// Starts a thread that protects the execution with a try/catch block.
        /// It appears that in .NET, unhandled exception in any thread causes the app to terminate
        /// http://msdn.microsoft.com/en-us/library/ms228965.aspx
        /// </summary>
        private void StartThread(ThreadStart main)
        {
            new Thread(delegate() {
                try
                {
                    main();
                }
                catch (Exception e)
                {
                    WriteEvent("Thread failed unexpectedly",e);
                }
            }).Start();
        }

        /// <summary>
        /// Handle the creation of the logfiles based on the optional logmode setting.
        /// </summary>
        private void HandleLogfiles()
        {
            string logDirectory = _descriptor.LogDirectory;

            if (!Directory.Exists(logDirectory))
            {
                Directory.CreateDirectory(logDirectory);
            }

            LogHandler logAppender = _descriptor.LogHandler;
            logAppender.EventLogger = this;
            logAppender.log(_process.StandardOutput.BaseStream, _process.StandardError.BaseStream);
        }

        public void LogEvent(String message)
        {
            if (_systemShuttingdown)
            {
                /* NOP - cannot call EventLog because of shutdown. */
            }
            else
            {
                try
                {
                    EventLog.WriteEntry(message);
                }
                catch (Exception e)
                {
                    WriteEvent("Failed to log event in Windows Event Log: " + message + "; Reason: ", e);
                }
            }
        }

        public void LogEvent(String message, EventLogEntryType type)
        {
            if (_systemShuttingdown)
            {
                /* NOP - cannot call EventLog because of shutdown. */
            }
            else
            {
                try
                {
                    EventLog.WriteEntry(message, type);
                }
                catch (Exception e)
                {
                    WriteEvent("Failed to log event in Windows Event Log. Reason: ", e);
                }
            }
        }

        internal void WriteEvent(Exception exception)
        {
            WriteEvent(exception.Message + "\nStacktrace:" + exception.StackTrace);
        }

        internal void WriteEvent(String message, Exception exception)
        {
            WriteEvent(message + "\nMessage:" + exception.Message + "\nStacktrace:" + exception.StackTrace);
        }

        internal void WriteEvent(String message)
        {
            string logfilename = Path.Combine(_descriptor.LogDirectory, _descriptor.BaseName + ".wrapper.log");
            StreamWriter log = new StreamWriter(logfilename, true);

            log.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - " + message);
            log.Flush();
            log.Close();
        }

        protected override void OnStart(string[] _)
        {
            _envs = _descriptor.EnvironmentVariables;
            foreach (string key in _envs.Keys)
            {
                LogEvent("envar " + key + '=' + _envs[key]);
            }

            FileOperations fops = new FileOperations(this, _descriptor);
            fops.HandleFileCopies();

            // handle downloads
            foreach (Download d in _descriptor.Downloads)
            {
                LogEvent("Downloading: " + d.From+ " to "+d.To);
                try
                {
                    d.Perform();
                }
                catch (Exception e)
                {
                    LogEvent("Failed to download " + d.From + " to " + d.To + "\n" + e.Message);
                    WriteEvent("Failed to download " + d.From +" to "+d.To, e);
                    // but just keep going
                }
            }

            string startarguments = _descriptor.Startarguments;

            if (startarguments == null)
            {
                startarguments = _descriptor.Arguments;
            }
            else
            {
                startarguments += " " + _descriptor.Arguments;
            }

            LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
            WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments);

            StartProcess(_process, startarguments, _descriptor.Executable);

            // send stdout and stderr to its respective output file.
            HandleLogfiles();

            _process.StandardInput.Close(); // nothing for you to read!
        }

        protected override void OnShutdown()
        {
//            WriteEvent("OnShutdown");

            try
            {
                _systemShuttingdown = true;
                StopIt();
            }
            catch (Exception ex)
            {
                WriteEvent("Shutdown exception", ex);
            }
        }

        protected override void OnStop()
        {
//            WriteEvent("OnStop");

            try
            {
                StopIt();
            }
            catch (Exception ex)
            {
                WriteEvent("Stop exception", ex);
            }
        }

        /// <summary>
        /// Called when we are told by Windows SCM to exit.
        /// </summary>
        private void StopIt()
        {
            string stoparguments = _descriptor.Stoparguments;
            LogEvent("Stopping " + _descriptor.Id);
            WriteEvent("Stopping " + _descriptor.Id);
            _orderlyShutdown = true;

            if (stoparguments == null)
            {
                try
                {
                    WriteEvent("ProcessKill " + _process.Id);
                    StopProcessAndChildren(_process.Id);
                }
                catch (InvalidOperationException)
                {
                    // already terminated
                }
            }
            else
            {
                SignalShutdownPending();

                stoparguments += " " + _descriptor.Arguments;

                Process stopProcess = new Process();
                String executable = _descriptor.StopExecutable;

                if (executable == null)
                {
                    executable = _descriptor.Executable;
                }

                StartProcess(stopProcess, stoparguments, executable);

                WriteEvent("WaitForProcessToExit "+_process.Id+"+"+stopProcess.Id);
                WaitForProcessToExit(_process);
                WaitForProcessToExit(stopProcess);
                SignalShutdownComplete();
            }

            if (_systemShuttingdown && _descriptor.BeepOnShutdown)
            {
                Console.Beep();
            }

            WriteEvent("Finished " + _descriptor.Id);
        }

        private void StopProcessAndChildren(int pid)
        {
            var childPids = GetChildPids(pid);

            if (_descriptor.StopParentProcessFirst)
            {
                StopProcess(pid);
                foreach (var childPid in childPids)
                {
                    StopProcessAndChildren(childPid);
                }
            }
            else
            {
                foreach (var childPid in childPids)
                {
                    StopProcessAndChildren(childPid);
                }
                StopProcess(pid);
            }
        }

        private List<int> GetChildPids(int pid)
        {
            var searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);
            var childPids = new List<int>();
            foreach (var mo in searcher.Get())
            {
                var childProcessId = mo["ProcessID"];
                WriteEvent("Found child process: " + childProcessId + " Name: " + mo["Name"]);
                childPids.Add(Convert.ToInt32(childProcessId));
            }
            return childPids;
        }

        private void StopProcess(int pid)
        {
            WriteEvent("Stopping process " + pid);
            Process proc;
            try
            {
                proc = Process.GetProcessById(pid);
            }
            catch (ArgumentException)
            {
                WriteEvent("Process " + pid + " is already stopped");
                return;
            }

            WriteEvent("Send SIGINT " + pid);
            bool successful = SigIntHelper.SendSIGINTToProcess(proc, _descriptor.StopTimeout);
            if (successful)
            {
                WriteEvent("SIGINT to" + pid + " successful");
            }
            else
            {
                try
                {
                    WriteEvent("SIGINT to " + pid + " failed - Killing as fallback");
                    proc.Kill();
                }
                catch (ArgumentException)
                {
                    // Process already exited.
                }
            }
        }

        private void WaitForProcessToExit(Process processoWait)
        {
            SignalShutdownPending();

            try
            {
//                WriteEvent("WaitForProcessToExit [start]");

                while (!processoWait.WaitForExit(_descriptor.SleepTime.Milliseconds))
                {
                    SignalShutdownPending();
//                    WriteEvent("WaitForProcessToExit [repeat]");
                }
            }
            catch (InvalidOperationException)
            {
                // already terminated
            }

//            WriteEvent("WaitForProcessToExit [finished]");
        }

        private void SignalShutdownPending()
        {
            IntPtr handle = ServiceHandle;
            _wrapperServiceStatus.checkPoint++;
            _wrapperServiceStatus.waitHint = _descriptor.WaitHint.Milliseconds;
//            WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
            _wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING;
            Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus);
        }

        private void SignalShutdownComplete()
        {
            IntPtr handle = ServiceHandle;
            _wrapperServiceStatus.checkPoint++;
//            WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
            _wrapperServiceStatus.currentState = (int)State.SERVICE_STOPPED;
            Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus);
        }

        private void StartProcess(Process processToStart, string arguments, String executable)
        {
            var ps = processToStart.StartInfo;
            ps.FileName = executable;
            ps.Arguments = arguments;
            ps.WorkingDirectory = _descriptor.WorkingDirectory;
            ps.CreateNoWindow = false;
            ps.UseShellExecute = false;
            ps.RedirectStandardInput = true; // this creates a pipe for stdin to the new process, instead of having it inherit our stdin.
            ps.RedirectStandardOutput = true;
            ps.RedirectStandardError = true;

            foreach (string key in _envs.Keys)
                Environment.SetEnvironmentVariable(key, _envs[key]);
                // ps.EnvironmentVariables[key] = envs[key]; // bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163)

            processToStart.Start();
            WriteEvent("Started " + processToStart.Id);

            var priority = _descriptor.Priority;
            if (priority != ProcessPriorityClass.Normal)
                processToStart.PriorityClass = priority;

            // monitor the completion of the process
            StartThread(delegate
            {
                string msg = processToStart.Id + " - " + processToStart.StartInfo.FileName + " " + processToStart.StartInfo.Arguments;
                processToStart.WaitForExit();

                try
                {
                    if (_orderlyShutdown)
                    {
                        LogEvent("Child process [" + msg + "] terminated with " + processToStart.ExitCode, EventLogEntryType.Information);
                    }
                    else
                    {
                        LogEvent("Child process [" + msg + "] finished with " + processToStart.ExitCode, EventLogEntryType.Warning);
                        // if we finished orderly, report that to SCM.
                        // by not reporting unclean shutdown, we let Windows SCM to decide if it wants to
                        // restart the service automatically
                        if (processToStart.ExitCode == 0)
                            SignalShutdownComplete();
                        Environment.Exit(processToStart.ExitCode);
                    }
                }
                catch (InvalidOperationException ioe)
                {
                    LogEvent("WaitForExit " + ioe.Message);
                }

                try
                {
                    processToStart.Dispose();
                }
                catch (InvalidOperationException ioe)
                {
                    LogEvent("Dispose " + ioe.Message);
                }
            });
        }

        public static int Main(string[] args)
        {
            try
            {
                Run(args);
                return 0;
            }
            catch (WmiException e)
            {
                Console.Error.WriteLine(e);
                return (int)e.ErrorCode;
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e);
                return -1;
            }
        }

        private static void ThrowNoSuchService()
        {
            throw new WmiException(ReturnValue.NoSuchService);
        }

        // ReSharper disable once InconsistentNaming
        public static void Run(string[] _args, ServiceDescriptor descriptor = null)
        {
            if (_args.Length > 0)
            {
                var d = descriptor ?? new ServiceDescriptor();
                Win32Services svc = new WmiRoot().GetCollection<Win32Services>();
                Win32Service s = svc.Select(d.Id);

                var args = new List<string>(Array.AsReadOnly(_args));
                if (args[0] == "/redirect")
                {
                    // Redirect output
                    // One might ask why we support this when the caller
                    // can redirect the output easily. The answer is for supporting UAC.
                    // On UAC-enabled Windows such as Vista, SCM operation requires
                    // elevated privileges, thus winsw.exe needs to be launched
                    // accordingly. This in turn limits what the caller can do,
                    // and among other things it makes it difficult for the caller
                    // to read stdout/stderr. Thus redirection becomes handy.
                    var f = new FileStream(args[1], FileMode.Create);
                    var w = new StreamWriter(f) {AutoFlush = true};
                    Console.SetOut(w);
                    Console.SetError(w);

                    var handle = f.Handle;
                    Kernel32.SetStdHandle(-11, handle); // set stdout
                    Kernel32.SetStdHandle(-12, handle); // set stder

                    args = args.GetRange(2, args.Count - 2);
                }

                args[0] = args[0].ToLower();
                if (args[0] == "install")
                {
                    // Check if the service exists
                    if (s != null)
                    {
                        Console.WriteLine("Service with id '" + d.Id + "' already exists");
                        Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file");
                        throw new Exception("Installation failure: Service with id '" + d.Id + "' already exists");
                    }

                    string username=null, password=null;
                    bool setallowlogonasaserviceright = false;
                    if (args.Count > 1 && args[1] == "/p")
                    {
                        // we expected username/password on stdin
                        Console.Write("Username: ");
                        username = Console.ReadLine();
                        Console.Write("Password: ");
                        password = ReadPassword();
                        Console.WriteLine();
                        Console.Write("Set Account rights to allow log on as a service (y/n)?: ");
                        var keypressed = Console.ReadKey();
                        Console.WriteLine();
                        if (keypressed.Key == ConsoleKey.Y)
                        {
                            setallowlogonasaserviceright = true;
                        }
                    }
                    else
                    {
                        if (d.HasServiceAccount())
                        {
                            username = d.ServiceAccountUser;
                            password = d.ServiceAccountPassword;
                            setallowlogonasaserviceright = d.AllowServiceAcountLogonRight;
                        }
                    }

                    if (setallowlogonasaserviceright)
                    {
                        LogonAsAService.AddLogonAsAServiceRight(username);
                    }

                    svc.Create (
                        d.Id,
                        d.Caption,
                        "\"" + d.ExecutablePath + "\"",
                        ServiceType.OwnProcess,
                        ErrorControl.UserNotified,
                        d.StartMode,
                        d.Interactive,
                        username,
                        password,
                        d.ServiceDependencies);

                    // update the description
                    /* Somehow this doesn't work, even though it doesn't report an error
                    Win32Service s = svc.Select(d.Id);
                    s.Description = d.Description;
                    s.Commit();
                     */

                    // so using a classic method to set the description. Ugly.
                    Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services")
                        .OpenSubKey(d.Id, true).SetValue("Description", d.Description);

                    var actions = d.FailureActions;
                    if (actions.Count > 0)
                    {// set the failure actions
                        using (ServiceManager scm = new ServiceManager())
                        {
                            using (Service sc = scm.Open(d.Id))
                            {
                                sc.ChangeConfig(d.ResetFailureAfter, actions);
                            }
                        }
                    }
                    return;
                }
                if (args[0] == "uninstall")
                {
                    if (s == null)
                    {
                        Console.WriteLine("Warning! The service with id '" + d.Id + "' does not exist. Nothing to uninstall");
                        return; // there's no such service, so consider it already uninstalled
                    }
                    try
                    {
                        s.Delete();
                    }
                    catch (WmiException e)
                    {
                        if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion)
                            return; // it's already uninstalled, so consider it a success
                        throw e;
                    }
                    return;
                }
                if (args[0] == "start")
                {
                    if (s == null) ThrowNoSuchService();
                    s.StartService();
                    return;
                }
                if (args[0] == "stop")
                {
                    if (s == null) ThrowNoSuchService();
                    s.StopService();
                    return;
                }
                if (args[0] == "restart")
                {
                    if (s == null)
                        ThrowNoSuchService();

                    if(s.Started)
                        s.StopService();

                    while (s.Started)
                    {
                        Thread.Sleep(1000);
                        s = svc.Select(d.Id);
                    }

                    s.StartService();
                    return;
                }
                if (args[0] == "restart!")
                {
                    // run restart from another process group. see README.md for why this is useful.

                    STARTUPINFO si = new STARTUPINFO();
                    PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

                    bool result = Kernel32.CreateProcess(null, d.ExecutablePath+" restart", IntPtr.Zero, IntPtr.Zero, false, 0x200/*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out pi);
                    if (!result)
                    {
                        throw new Exception("Failed to invoke restart: "+Marshal.GetLastWin32Error());
                    }
                    return;
                }
                if (args[0] == "status")
                {
                    if (s == null)
                        Console.WriteLine("NonExistent");
                    else if (s.Started)
                        Console.WriteLine("Started");
                    else
                        Console.WriteLine("Stopped");
                    return;
                }
                if (args[0] == "test")
                {
                    WrapperService wsvc = new WrapperService();
                    wsvc.OnStart(args.ToArray());
                    Thread.Sleep(1000);
                    wsvc.OnStop();
                    return;
                }
                if (args[0] == "help" || args[0] == "--help" || args[0] == "-h"
                    || args[0] == "-?" || args[0] == "/?")
                {
                    printHelp();
                    return;
                }
                if (args[0] == "version")
                {
                    printVersion();
                    return;
                }

                Console.WriteLine("Unknown command: " + args[0]);
                printAvailableCommandsInfo();
                throw new Exception("Unknown command: " + args[0]);

            }
            Run(new WrapperService());
        }

        private static string ReadPassword()
        {
            StringBuilder buf = new StringBuilder();
            ConsoleKeyInfo key;
            while (true)
            {
                key = Console.ReadKey(true);
                if (key.Key == ConsoleKey.Enter)
                {
                    return buf.ToString();
                }
                else if (key.Key == ConsoleKey.Backspace)
                {
                    buf.Remove(buf.Length - 1, 1);
                    Console.Write("\b \b");
                }
                else
                {
                    Console.Write('*');
                    buf.Append(key.KeyChar);
                }
            }
        }

        private static void printHelp()
        {
            Console.WriteLine("A wrapper binary that can be used to host executables as Windows services");
            Console.WriteLine("");
            Console.WriteLine("Usage: winsw [/redirect file] <command> [<args>]");
            Console.WriteLine("       Missing arguments trigger the service mode");
            Console.WriteLine("");
            printAvailableCommandsInfo();
            Console.WriteLine("");
            Console.WriteLine("Extra options:");
            Console.WriteLine("- '/redirect' - redirect the wrapper's STDOUT and STDERR to the specified file");
            Console.WriteLine("");
            printVersion();
            Console.WriteLine("More info: https://github.com/kohsuke/winsw");
            Console.WriteLine("Bug tracker: https://github.com/kohsuke/winsw/issues");
        }

        //TODO: Rework to enum in winsw-2.0
        private static void printAvailableCommandsInfo()
        {
            Console.WriteLine("Available commands:");
            Console.WriteLine("- 'install'   - install the service to Windows Service Controller");
            Console.WriteLine("- 'uninstall' - uninstall the service");
            Console.WriteLine("- 'start'     - start the service (must be installed before)");
            Console.WriteLine("- 'stop'      - stop the service");
            Console.WriteLine("- 'restart'   - restart the service");
            Console.WriteLine("- 'restart!'  - self-restart (can be called from child processes)");
            Console.WriteLine("- 'status'    - check the current status of the service");
            Console.WriteLine("- 'test'      - check if the service can be started and then stopped");
            Console.WriteLine("- 'version'   - print the version info");
            Console.WriteLine("- 'help'      - print the help info (aliases: -h,--help,-?,/?)");
        }

        private static void printVersion()
        {
            Console.WriteLine("WinSW " + Version);
        }
    }
}

redpajama_set_name: RedPajamaGithub


text:
"""
Implements main window of VN Trader.
"""

import webbrowser
from functools import partial
from importlib import import_module
from typing import Callable, Dict, Tuple

from PyQt5 import QtCore, QtGui, QtWidgets

from vnpy.event import EventEngine
from .widget import (
    TickMonitor,
    OrderMonitor,
    TradeMonitor,
    PositionMonitor,
    AccountMonitor,
    LogMonitor,
    ActiveOrderMonitor,
    ConnectDialog,
    ContractManager,
    TradingWidget,
    AboutDialog,
    GlobalDialog
)
from .editor import CodeEditor
from ..engine import MainEngine
from ..utility import get_icon_path, TRADER_DIR

class MainWindow(QtWidgets.QMainWindow):
    """
    Main window of VN Trader.
    """

    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        """"""
        super(MainWindow, self).__init__()
        self.main_engine: MainEngine = main_engine
        self.event_engine: EventEngine = event_engine

        self.window_title = f"VN Trader [{TRADER_DIR}]"

        self.connect_dialogs = {}
        self.widgets: Dict[str, QtWidgets.QWidget] = {}

        self.trading_widget = None
        self.position_widget = None
        self.tick_widget = None

        self.init_ui()

    def init_ui(self) -> None:
        """"""
        self.setWindowTitle(self.window_title)
        self.init_dock()
        self.init_toolbar()
        self.init_menu()
        self.load_window_setting("custom")

        # 绑定行情双击 ~ 交易界面自动填写
        if self.tick_widget and self.trading_widget:
            self.tick_widget.itemDoubleClicked.connect(self.trading_widget.auto_fill_symbol)
        if self.position_widget and self.trading_widget:
            self.position_widget.itemDoubleClicked.connect(self.trading_widget.close_position)

    def init_dock(self) -> None:
        """"""
        self.trading_widget, trading_dock = self.create_dock(
            TradingWidget, "交易", QtCore.Qt.LeftDockWidgetArea
        )
        self.tick_widget, tick_dock = self.create_dock(
            TickMonitor, "行情", QtCore.Qt.RightDockWidgetArea
        )
        order_widget, order_dock = self.create_dock(
            OrderMonitor, "委托", QtCore.Qt.RightDockWidgetArea
        )
        active_widget, active_dock = self.create_dock(
            ActiveOrderMonitor, "活动", QtCore.Qt.RightDockWidgetArea
        )
        trade_widget, trade_dock = self.create_dock(
            TradeMonitor, "成交", QtCore.Qt.RightDockWidgetArea
        )
        log_widget, log_dock = self.create_dock(
            LogMonitor, "日志", QtCore.Qt.BottomDockWidgetArea
        )
        account_widget, account_dock = self.create_dock(
            AccountMonitor, "资金", QtCore.Qt.BottomDockWidgetArea
        )
        self.position_widget, position_dock = self.create_dock(
            PositionMonitor, "持仓", QtCore.Qt.BottomDockWidgetArea
        )

        self.tabifyDockWidget(active_dock, order_dock)

        self.save_window_setting("default")

    def init_menu(self) -> None:
        """"""
        bar = self.menuBar()

        # System menu
        sys_menu = bar.addMenu("系统")

        gateway_names = self.main_engine.get_all_gateway_names()
        for name in gateway_names:
            func = partial(self.connect, name)
            self.add_menu_action(sys_menu, f"连接{name}", "connect.ico", func)

        sys_menu.addSeparator()

        self.add_menu_action(sys_menu, "退出", "exit.ico", self.close)

        # App menu
        app_menu = bar.addMenu("功能")

        all_apps = self.main_engine.get_all_apps()
        for app in all_apps:
            try:
                if getattr(app, 'widget_name') is None:
                    continue

                ui_module = import_module(app.app_module + ".ui")
                widget_class = getattr(ui_module, app.widget_name)

                func = partial(self.open_widget, widget_class, app.app_name)
                icon_path = str(app.app_path.joinpath("ui", app.icon_name))
                self.add_menu_action(
                    app_menu, app.display_name, icon_path, func
                )
                self.add_toolbar_action(
                    app.display_name, icon_path, func
                )
            except Exception as ex:
                print(f'加载{app.app_module}出现异常:{str(ex)}')
        # Global setting editor
        action = QtWidgets.QAction("配置", self)
        action.triggered.connect(self.edit_global_setting)
        bar.addAction(action)

        # Help menu
        help_menu = bar.addMenu("帮助")

        self.add_menu_action(
            help_menu,
            "查询合约",
            "contract.ico",
            partial(self.open_widget, ContractManager, "contract"),
        )
        self.add_toolbar_action(
            "查询合约",
            "contract.ico",
            partial(self.open_widget, ContractManager, "contract")
        )
        self.add_menu_action(
            help_menu,
            "保存合约",
            "contract.ico",
            self.save_contracts
        )

        self.add_menu_action(
            help_menu,
            "代码编辑",
            "editor.ico",
            partial(self.open_widget, CodeEditor, "editor")
        )
        self.add_toolbar_action(
            "代码编辑",
            "editor.ico",
            partial(self.open_widget, CodeEditor, "editor")
        )

        self.add_menu_action(
            help_menu, "还原窗口", "restore.ico", self.restore_window_setting
        )

        self.add_menu_action(
            help_menu, "测试邮件", "email.ico", self.send_test_email
        )

        self.add_menu_action(
            help_menu, "社区论坛", "forum.ico", self.open_forum
        )
        self.add_toolbar_action(
            "社区论坛", "forum.ico", self.open_forum
        )

        self.add_menu_action(
            help_menu,
            "关于",
            "about.ico",
            partial(self.open_widget, AboutDialog, "about"),
        )

    def init_toolbar(self) -> None:
        """"""
        self.toolbar = QtWidgets.QToolBar(self)
        self.toolbar.setObjectName("工具栏")
        self.toolbar.setFloatable(False)
        self.toolbar.setMovable(False)

        # Set button size
        w = 40
        size = QtCore.QSize(w, w)
        self.toolbar.setIconSize(size)

        # Set button spacing
        self.toolbar.layout().setSpacing(10)

        self.addToolBar(QtCore.Qt.LeftToolBarArea, self.toolbar)

    def add_menu_action(
        self,
        menu: QtWidgets.QMenu,
        action_name: str,
        icon_name: str,
        func: Callable,
    ) -> None:
        """"""
        icon = QtGui.QIcon(get_icon_path(__file__, icon_name))

        action = QtWidgets.QAction(action_name, self)
        action.triggered.connect(func)
        action.setIcon(icon)

        menu.addAction(action)

    def add_toolbar_action(
        self,
        action_name: str,
        icon_name: str,
        func: Callable,
    ) -> None:
        """"""
        icon = QtGui.QIcon(get_icon_path(__file__, icon_name))

        action = QtWidgets.QAction(action_name, self)
        action.triggered.connect(func)
        action.setIcon(icon)

        self.toolbar.addAction(action)

    def create_dock(
        self, widget_class: QtWidgets.QWidget, name: str, area: int
    ) -> Tuple[QtWidgets.QWidget, QtWidgets.QDockWidget]:
        """
        Initialize a dock widget.
        """
        widget = widget_class(self.main_engine, self.event_engine)

        dock = QtWidgets.QDockWidget(name)
        dock.setWidget(widget)
        dock.setObjectName(name)
        dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable)
        self.addDockWidget(area, dock)
        return widget, dock

    def connect(self, gateway_name: str) -> None:
        """
        Open connect dialog for gateway connection.
        """
        dialog = self.connect_dialogs.get(gateway_name, None)
        if not dialog:
            dialog = ConnectDialog(self.main_engine, gateway_name)

        dialog.exec_()

    def closeEvent(self, event: QtGui.QCloseEvent) -> None:
        """
        Call main engine close function before exit.
        """
        reply = QtWidgets.QMessageBox.question(
            self,
            "退出",
            "确认退出?",
            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
            QtWidgets.QMessageBox.No,
        )

        if reply == QtWidgets.QMessageBox.Yes:
            for widget in self.widgets.values():
                widget.close()
            self.save_window_setting("custom")

            self.main_engine.close()

            event.accept()
        else:
            event.ignore()

    def open_widget(self, widget_class: QtWidgets.QWidget, name: str) -> None:
        """
        Open contract manager.
        """
        widget = self.widgets.get(name, None)
        if not widget:
            widget = widget_class(self.main_engine, self.event_engine)
            self.widgets[name] = widget

        if isinstance(widget, QtWidgets.QDialog):
            widget.exec_()
        else:
            widget.show()

    def save_window_setting(self, name: str):
        """
        Save current window size and state by trader path and setting name.
        """
        settings = QtCore.QSettings(self.window_title, name)
        settings.setValue("state", self.saveState())
        settings.setValue("geometry", self.saveGeometry())

    def load_window_setting(self, name: str) -> None:
        """
        Load previous window size and state by trader path and setting name.
        """
        settings = QtCore.QSettings(self.window_title, name)
        state = settings.value("state")
        geometry = settings.value("geometry")

        if isinstance(state, QtCore.QByteArray):
            self.restoreState(state)
            self.restoreGeometry(geometry)

    def save_contracts(self) -> None:
        self.main_engine.save_contracts()

    def restore_window_setting(self) -> None:
        """
        Restore window to default setting.
        """
        self.load_window_setting("default")
        self.showMaximized()

    def send_test_email(self) -> None:
        """
        Sending a test email.
        """
        self.main_engine.send_email("VN Trader", "testing")

    def open_forum(self) -> None:
        """
        """
        webbrowser.open("https://www.vnpy.com/forum/")

    def edit_global_setting(self) -> None:
        """
        """
        dialog = GlobalDialog()
        dialog.exec_()


text:
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/holding_space/holding_space_tray_child_bubble.h"

#include <set>

#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/style/color_provider.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/holding_space/holding_space_item_views_section.h"
#include "ash/system/holding_space/holding_space_util.h"
#include "ash/system/holding_space/holding_space_view_delegate.h"
#include "ash/system/tray/tray_constants.h"
#include "base/bind.h"
#include "ui/compositor/callback_layer_animation_observer.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/views/layout/box_layout.h"

namespace ash {

namespace {

// Animation.
constexpr base::TimeDelta kAnimationDuration = base::Milliseconds(167);

// Helpers ---------------------------------------------------------------------

// Returns a callback which deletes the associated animation observer after
// running another `callback`.
using AnimationCompletedCallback = base::OnceCallback<void(bool aborted)>;
base::RepeatingCallback<bool(const ui::CallbackLayerAnimationObserver&)>
DeleteObserverAfterRunning(AnimationCompletedCallback callback) {
  return base::BindRepeating(
      [](AnimationCompletedCallback callback,
         const ui::CallbackLayerAnimationObserver& observer) {
        // NOTE: It's safe to move `callback` since this code will only run
        // once due to deletion of the associated `observer`. The `observer` is
        // deleted by returning `true`.
        std::move(callback).Run(/*aborted=*/observer.aborted_count() > 0);
        return true;
      },
      base::Passed(std::move(callback)));
}

// Returns whether the given holding space `model` contains any initialized
// items which are supported by the specified holding space item views
// `section`.
bool ModelContainsInitializedItemsForSection(
    const HoldingSpaceModel* model,
    const HoldingSpaceItemViewsSection* section) {
  const auto& supported_types = section->supported_types();
  return std::any_of(
      supported_types.begin(), supported_types.end(),
      [&model](HoldingSpaceItem::Type supported_type) {
        return model->ContainsInitializedItemOfType(supported_type);
      });
}

// TopAlignedBoxLayout ---------------------------------------------------------

// A vertical `views::BoxLayout` which overrides layout behavior when there is
// insufficient layout space to accommodate all children's preferred sizes.
// Unlike `views::BoxLayout` which will not allow children to exceed its content
// bounds, TopAlignedBoxLayout will ensure that children still receive their
// preferred sizes. This prevents layout jank that would otherwise occur when
// the host view's bounds are being animated due to content changes.
class TopAlignedBoxLayout : public views::BoxLayout {
 public:
  TopAlignedBoxLayout(const gfx::Insets& insets, int spacing)
      : views::BoxLayout(views::BoxLayout::Orientation::kVertical,
                         insets,
                         spacing) {}

 private:
  // views::BoxLayout:
  void Layout(views::View* host) override {
    if (host->height() >= host->GetPreferredSize().height()) {
      views::BoxLayout::Layout(host);
      return;
    }

    gfx::Rect contents_bounds(host->GetContentsBounds());
    contents_bounds.Inset(inside_border_insets());

    // If we only have a single child view and that child view is okay with
    // being sized arbitrarily small, short circuit layout logic and give that
    // child all available layout space. This is the case for the
    // `PinnedFileSection` which supports scrolling its content when necessary.
    if (host->children().size() == 1u &&
        host->children()[0]->GetMinimumSize().IsEmpty()) {
      host->children()[0]->SetBoundsRect(contents_bounds);
      return;
    }

    int top = contents_bounds.y();
    int left = contents_bounds.x();
    int width = contents_bounds.width();

    for (views::View* child : host->children()) {
      gfx::Size size(width, child->GetHeightForWidth(width));
      child->SetBounds(left, top, size.width(), size.height());
      top += size.height() + between_child_spacing();
    }
  }
};

}  // namespace

// HoldingSpaceTrayChildBubble -------------------------------------------------

HoldingSpaceTrayChildBubble::HoldingSpaceTrayChildBubble(
    HoldingSpaceViewDelegate* delegate)
    : delegate_(delegate) {
  controller_observer_.Observe(HoldingSpaceController::Get());
  if (HoldingSpaceController::Get()->model())
    model_observer_.Observe(HoldingSpaceController::Get()->model());
}

HoldingSpaceTrayChildBubble::~HoldingSpaceTrayChildBubble() = default;

void HoldingSpaceTrayChildBubble::Init() {
  // Layout.
  SetLayoutManager(std::make_unique<TopAlignedBoxLayout>(
      kHoldingSpaceChildBubblePadding, kHoldingSpaceChildBubbleChildSpacing));

  // Layer.
  SetPaintToLayer(ui::LAYER_SOLID_COLOR);
  layer()->GetAnimator()->set_preemption_strategy(
      ui::LayerAnimator::PreemptionStrategy::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
  layer()->SetFillsBoundsOpaquely(false);
  layer()->SetIsFastRoundedCorner(true);
  layer()->SetOpacity(0.f);
  layer()->SetRoundedCornerRadius(
      gfx::RoundedCornersF{kUnifiedTrayCornerRadius});

  // Sections.
  for (auto& section : CreateSections()) {
    sections_.push_back(AddChildView(std::move(section)));
    sections_.back()->Init();
  }
}

void HoldingSpaceTrayChildBubble::Reset() {
  // Prevent animation callbacks from running when the holding space bubble is
  // being asynchronously closed. This view will be imminently deleted.
  weak_factory_.InvalidateWeakPtrs();

  model_observer_.Reset();
  controller_observer_.Reset();

  for (HoldingSpaceItemViewsSection* section : sections_)
    section->Reset();
}

std::vector<HoldingSpaceItemView*>
HoldingSpaceTrayChildBubble::GetHoldingSpaceItemViews() {
  std::vector<HoldingSpaceItemView*> views;
  for (HoldingSpaceItemViewsSection* section : sections_) {
    auto section_views = section->GetHoldingSpaceItemViews();
    views.insert(views.end(), section_views.begin(), section_views.end());
  }
  return views;
}

void HoldingSpaceTrayChildBubble::OnHoldingSpaceModelAttached(
    HoldingSpaceModel* model) {
  model_observer_.Observe(model);

  // New model contents, if available, will be populated and animated in after
  // the out animation completes.
  MaybeAnimateOut();
}

void HoldingSpaceTrayChildBubble::OnHoldingSpaceModelDetached(
    HoldingSpaceModel* model) {
  model_observer_.Reset();
  MaybeAnimateOut();
}

void HoldingSpaceTrayChildBubble::OnHoldingSpaceItemsAdded(
    const std::vector<const HoldingSpaceItem*>& items) {
  // Ignore new items while the bubble is animating out. The bubble content will
  // be updated to match the model after the out animation completes.
  if (!is_animating_out_) {
    for (HoldingSpaceItemViewsSection* section : sections_)
      section->OnHoldingSpaceItemsAdded(items);
  }
}

void HoldingSpaceTrayChildBubble::OnHoldingSpaceItemsRemoved(
    const std::vector<const HoldingSpaceItem*>& items) {
  // Ignore item removal while the bubble is animating out. The bubble content
  // will be updated to match the model after the out animation completes.
  if (is_animating_out_)
    return;

  HoldingSpaceModel* model = HoldingSpaceController::Get()->model();
  DCHECK(model);

  // This child bubble should animate out if the attached model does not
  // contain initialized items supported by any of its sections. The exception
  // is if a section has a placeholder to show in lieu of holding space items.
  // If a placeholder exists, the child bubble should persist.
  const bool animate_out = std::none_of(
      sections_.begin(), sections_.end(),
      [&model](const HoldingSpaceItemViewsSection* section) {
        return section->has_placeholder() ||
               ModelContainsInitializedItemsForSection(model, section);
      });

  if (animate_out) {
    MaybeAnimateOut();
    return;
  }

  for (HoldingSpaceItemViewsSection* section : sections_)
    section->OnHoldingSpaceItemsRemoved(items);
}

void HoldingSpaceTrayChildBubble::OnHoldingSpaceItemInitialized(
    const HoldingSpaceItem* item) {
  // Ignore item initialized while the bubble is animating out. The bubble
  // content will be updated to match the model after the out animation
  // completes.
  if (!is_animating_out_) {
    for (HoldingSpaceItemViewsSection* section : sections_)
      section->OnHoldingSpaceItemInitialized(item);
  }
}

const char* HoldingSpaceTrayChildBubble::GetClassName() const {
  return "HoldingSpaceTrayChildBubble";
}

void HoldingSpaceTrayChildBubble::ChildPreferredSizeChanged(
    views::View* child) {
  PreferredSizeChanged();
}

void HoldingSpaceTrayChildBubble::ChildVisibilityChanged(views::View* child) {
  if (ignore_child_visibility_changed_)
    return;

  // This child bubble should be visible iff it has visible children.
  bool visible = false;
  for (const views::View* c : children()) {
    if (c->GetVisible()) {
      visible = true;
      break;
    }
  }

  if (visible != GetVisible()) {
    SetVisible(visible);

    // When the child bubble becomes visible, its due to one of its sections
    // becoming visible. In this case, the child bubble should animate in.
    if (GetVisible())
      MaybeAnimateIn();
  }

  PreferredSizeChanged();
}

void HoldingSpaceTrayChildBubble::OnGestureEvent(ui::GestureEvent* event) {
  delegate_->OnHoldingSpaceTrayChildBubbleGestureEvent(*event);
  views::View::OnGestureEvent(event);
}

bool HoldingSpaceTrayChildBubble::OnMousePressed(const ui::MouseEvent& event) {
  delegate_->OnHoldingSpaceTrayChildBubbleMousePressed(event);
  return true;
}

void HoldingSpaceTrayChildBubble::OnThemeChanged() {
  views::View::OnThemeChanged();
  layer()->SetColor(AshColorProvider::Get()->GetBaseLayerColor(
      AshColorProvider::BaseLayerType::kTransparent80));
}

void HoldingSpaceTrayChildBubble::MaybeAnimateIn() {
  // Don't preempt an out animation as new content will populate and be animated
  // in, if any exists, once the out animation completes.
  if (is_animating_out_)
    return;

  // Don't attempt to animate in this bubble unnecessarily as it will cause
  // opacity to revert to zero before proceeding to animate in. Ensure that
  // event processing is enabled as it may have been disabled while animating
  // this bubble out.
  if (layer()->GetTargetOpacity() == 1.f) {
    SetCanProcessEventsWithinSubtree(true);
    return;
  }

  // NOTE: `animate_in_observer` is deleted after `OnAnimateInCompleted()`.
  ui::CallbackLayerAnimationObserver* animate_in_observer =
      new ui::CallbackLayerAnimationObserver(DeleteObserverAfterRunning(
          base::BindOnce(&HoldingSpaceTrayChildBubble::OnAnimateInCompleted,
                         weak_factory_.GetWeakPtr())));

  AnimateIn(animate_in_observer);
  animate_in_observer->SetActive();
}

void HoldingSpaceTrayChildBubble::MaybeAnimateOut() {
  if (is_animating_out_)
    return;

  // Child bubbles should not process events when being animated as the model
  // objects backing their views may no longer exist. Event processing will be
  // re-enabled on animation completion.
  SetCanProcessEventsWithinSubtree(false);

  // NOTE: `animate_out_observer` is deleted after `OnAnimateOutCompleted()`.
  ui::CallbackLayerAnimationObserver* animate_out_observer =
      new ui::CallbackLayerAnimationObserver(DeleteObserverAfterRunning(
          base::BindOnce(&HoldingSpaceTrayChildBubble::OnAnimateOutCompleted,
                         weak_factory_.GetWeakPtr())));

  AnimateOut(animate_out_observer);
  animate_out_observer->SetActive();
}

void HoldingSpaceTrayChildBubble::AnimateIn(
    ui::LayerAnimationObserver* observer) {
  DCHECK(!is_animating_out_);

  // Animation is only necessary if this view is visible to the user.
  const base::TimeDelta animation_duration =
      IsDrawn() ? kAnimationDuration : base::TimeDelta();

  // Delay in animations to give the holding space bubble time to animate its
  // layout changes. This ensures that there is sufficient space to display the
  // child bubble before it is displayed to the user.
  const base::TimeDelta animation_delay =
      IsDrawn() ? kAnimationDuration : base::TimeDelta();
  holding_space_util::AnimateIn(this, animation_duration, animation_delay,
                                observer);
}

void HoldingSpaceTrayChildBubble::AnimateOut(
    ui::LayerAnimationObserver* observer) {
  DCHECK(!is_animating_out_);
  is_animating_out_ = true;

  // Animation is only necessary if this view is visible to the user.
  const base::TimeDelta animation_duration =
      IsDrawn() ? kAnimationDuration : base::TimeDelta();
  holding_space_util::AnimateOut(this, animation_duration, observer);
}

void HoldingSpaceTrayChildBubble::OnAnimateInCompleted(bool aborted) {
  // Restore event processing once the child bubble has fully animated in. Its
  // contents are guaranteed to exist in the model and can be acted upon by the
  // user.
  if (!aborted)
    SetCanProcessEventsWithinSubtree(true);
}

void HoldingSpaceTrayChildBubble::OnAnimateOutCompleted(bool aborted) {
  DCHECK(is_animating_out_);
  is_animating_out_ = false;

  if (aborted)
    return;

  // Once the child bubble has animated out it is transparent but still
  // "visible" as far as the views framework is concerned and so takes up layout
  // space. Hide the view so that the holding space bubble will animate the
  // re-layout of its view hierarchy with this child bubble taking no space.
  SetVisible(false);

  {
    // Removing all holding space items from a section will cause it to become
    // invisible. When multiple sections exist, the `ChildVisibilityChanged()`
    // event which results would normally cause this child bubble to regain
    // visibility since some sections have not yet been hidden. To prevent this
    // child bubble from becoming visible, ignore `ChildVisibilityChanged()`.
    base::AutoReset<bool> scoped_ignore_child_visibility_changed(
        &ignore_child_visibility_changed_, true);
    for (HoldingSpaceItemViewsSection* section : sections_)
      section->RemoveAllHoldingSpaceItemViews();
  }

  HoldingSpaceModel* model = HoldingSpaceController::Get()->model();
  if (!model || model->items().empty())
    return;

  std::vector<const HoldingSpaceItem*> item_ptrs;
  for (const auto& item : model->items())
    item_ptrs.push_back(item.get());

  // Populating a `section` may cause it's visibility to change if the `model`
  // contains initialized items of types which it supports. This, in turn, will
  // cause visibility of this child bubble to update and animate in if needed.
  for (HoldingSpaceItemViewsSection* section : sections_)
    section->OnHoldingSpaceItemsAdded(item_ptrs);
}

}  // namespace ash


text:
var gulp = require('gulp');
var paths = require('../paths');
const tslint = require('gulp-tslint');
var stylish = require('gulp-tslint-stylish');

gulp.task('lint', function() {
  return gulp.src(paths.source)
    .pipe(tslint())
    .pipe(tslint.report(stylish, {
        emitError: false,
        sort: true,
        bell: true
      }));
});


text:
package jdepend.metadata.tree;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import jdepend.metadata.JavaClass;

public abstract class JavaClassTreeCreator {

	private Collection<JavaClass> javaClasses = new HashSet<JavaClass>();// 记录扫描过的JavaClass

	JavaClassTreeCreator() {
	}

	public JavaClassTree create(JavaClass rootClass) {
		JavaClassTree tree = new JavaClassTree(rootClass);
		javaClasses.add(rootClass);
		this.rout(rootClass, tree);
		return tree;
	}

	private void rout(JavaClass javaClass, JavaClassTree tree) {

		List<JavaClass> dependClasses = new ArrayList<JavaClass>();
		// 广度搜索
		for (JavaClass dependClass : getRelationClass(javaClass)) {
			if (!javaClasses.contains(dependClass)) {
				javaClasses.add(dependClass);
				dependClasses.add(dependClass);
				tree.addNode(javaClass, dependClass);
			}
		}
		// 深度搜索
		for (JavaClass dependClass1 : dependClasses) {
			rout(dependClass1, tree);
		}
	}

	/**
	 * 得到指定与该类有关系的类集合
	 *
	 * @param javaClass
	 * @return
	 */
	protected abstract Collection<JavaClass> getRelationClass(JavaClass javaClass);
}


text:
using System;
using Server;
using Server.Engines.Craft;

namespace Server.Items
{
	public class Inshave : BaseTool
	{
		public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } }

		[Constructable]
		public Inshave() : base( 0x10E6 )
		{
			Weight = 1.0;
		}

		[Constructable]
		public Inshave( int uses ) : base( uses, 0x10E6 )
		{
			Weight = 1.0;
		}

		public Inshave( Serial serial ) : base( serial )
		{
		}

		public override void Serialize( GenericWriter writer )
		{
			base.Serialize( writer );

			writer.Write( (int) 0 ); // version
		}

		public override void Deserialize( GenericReader reader )
		{
			base.Deserialize( reader );

			int version = reader.ReadInt();
		}
	}
}


text:
{
  title: "Troubleshooting Common Error Messages",
  description: "Common Issues When Running Tests on Sauce",
  category: "Reference",
  index: 7
}

If the below troubleshooting steps don't help, please contact our
[support team](mailto:help@saucelabs.com).

##Sauce Labs Authentication Error.

A variation of the following error message will be shown by your test:
```
Sauce Labs Authentication Error.
You used username 'None' and access key 'None' to authenticate, which are not valid Sauce Labs credentials.

The following desired capabilities were received:
...
```

This indicates that your Sauce Labs username and access key aren't provided by
your tests as expected. To address this, please [follow the right tutorial for
your programming language](https://docs.saucelabs.com/).

##User Terminated

This message is used for tests manually interrupted using the **Cancel** or
**Breakpoint** buttons in our website. Since both of these take control of
the virtual machine immediately, test assets like screenshots, video, or logs
that require additional execution time will not be collected and made
available afterwards.

##Timeout errors

Sauce Labs implements several types of timeouts for automated tests as safety
nets to prevent unwanted consumption of minutes by runaway or broken tests.

You can set these to different values using "desired capabilities" settings in
the setup of your test, as described in the
[timeout configuration docs](https://saucelabs.com/docs/additional-config#timeouts).

To better understand why these timeouts exist, let's look at the lifecycle of
a Selenium command through the Sauce Labs service:

![Command Lifecycle](/images/reference/common_errors/selenium-command-lifecycle.png)

Here you can see the different types of interactions that happen in real time
between the test script running in your own infrastructure, Sauce Labs' service
and the Selenium or Appium server running inside our virtual machines.

Here are some common timeout errors you may find, what causes them and how to
address them:

###Test did not see a new command for 90 seconds. Timing out

This error is shown when Sauce Labs doesn't receive a new command from your
Selenium script in more than 90 seconds (the timeout's default length.)

Using the previously shown graph as a reference, this would happen if **step
number 5 never happened**. Without such a timeout, any tests that lack a proper
session ending request (generally rendered as a call to `driver.quit()` or
`browser.stop()`) will keep running forever, consuming all test minutes
available in your account.

The most common cause for this is customers' **test scripts crashing, getting
forcefully interrupted or losing connectivity**.

A less common but still possible cause is tests legitimately needing more than
90 seconds to send a new command to the browser. This happens most often when
**network or disk IO occurs in between selenium API calls in your tests** (DB
queries, local file reads or changes.)

If that's the case, our `idleTimeout` desired capability can be used to modify
Sauce's wait time for further commands. Check our
[idleTimeout docs](https://docs.saucelabs.com/reference/test-configuration/#idle-test-timeout)
for more details on this.

###Selenium didn't complete your last command on time

This error is shown when Sauce Labs doesn't receive a response from Selenium to
your Script's last command in more than 5 minutes (the timeout's default length.)

Using the previously shown graph as a reference, this would happen if **step
number 3 never happened**. Without such a timeout, any tests in which the
Selenium, Appium server or browser crashes would keep running forever,
consuming all test minutes available in your account.

The most common causes for this are **unresponsive javascript in your
application** or **a bug in Selenium/Appium**.

A less common but still possible cause is Selenium or Appium legitimately
needing more than 5 minutes to run your command. If that's the case, our
`commandTimeout` desired capability can be used to have Sauce wait longer for
your commands to complete in Selenium. Check our
[commandTimeout docs](https://docs.saucelabs.com/reference/test-configuration/#command-timeout)
for more details on this.

###Test exceeded maximum duration after 1800 seconds

This error is shown when Sauce Labs finds an automated test still running after
30 minutes (the timeout's default length.)

The most common cause for this is **an infinite loop in your tests** that keep
sending commands without an end clause.

It's not rare to find cases where tests legitimately need more than 30 minutes
to complete. If that's the case, our `maxDuration` desired capability can be
used to have Sauce wait longer for your test to finish. Check our
[maxDuration docs](https://docs.saucelabs.com/reference/test-configuration/#maximum-test-duration)
for more details on this.

##ERROR user closed connection while waiting for command to complete

Similar to our commandTimeout, this means that Selenium took a long time to run
the command -- usually the act of loading a page -- and your test runner shut
down the test before it might have timed out on Sauce's end. We recommend
checking into why the page would take a long time to load (perhaps because of
perpetually-loading items from another domain), and if need be, extending the
related timeout setting in your test runner.

##UnreachableBrowserException: Error communicating with the remote browser. It may have died

This error is thrown in Java when your test has timed out -- for example, due to
the automatic 90-second idle timeout, which most often is caused by a dropped
Internet connection. Then, the test script attempts to send another command to
Sauce, but the job has already finished, so the VM is no longer available. Check
your Internet connection, and try running your tests from a machine with a wired
Ethernet connection.

##The connection with your vm was lost and your job can't complete. You won't be charged for these minutes

This message occurs when our infrastructure loses communication with our vm and can't regain that connection after a reasonable time. If you only get this message rarely and randomly, it is probably a fluke on our end caused by an infrastructure blip.

However, if you are experiencing this error repeatedly for a specific test or set of tests there may be an issue on your end that is causing the failure. For example, if the error regularly appears after a specific selenium command there could be something wrong with the test that is causing selenium to crash. We have also seen issues with prerun executables: your script could be crashing the vm in any number of exciting ways, but the most common we've seen is consuming too much memory. We even had a situation once where a customer's script killed processes in a windows session, including the process we use to run jobs!

##The VM's disk has filled up. Selenium likely crashed as a result.

Our VMs have virtual disks which, just like hardware disks, can fill up. We make sure that our virtual machines have at least 3G free when we start a job, but sometimes complex/long-running tests fill up the guest machine's allocated space. This causes selenium to crash, which ends your test.

The best thing you can do to avoid this failure is to break out your long tests into shorter tests and/or make sure that your tests are not filling up a lot of disk space on the VM.

##Unsupported OS/browser/version combo

Check to make sure that the browser, version, and platform settings
you're using are in our
[supported list of platforms](https://saucelabs.com/docs/platforms).

Note that there's a
[separate list for Appium tests](https://saucelabs.com/docs/platforms/appium).

Relatedly, you may see:

```
The requested combination of browser, version and OS is unsupported by the Selenium version requested and would lead to a test failure.
Please set a different Selenium version, or just don't set it at all to default
to a working version...
```

This error usually means that you've manually set a Selenium version that is too
old to handle a more recent browser version. It should be resolved by choosing
a newer version of Selenium, or omitting this setting altogether.

##The Sauce VMs failed to start the browser or device

The twin sibling of "Unsupported version", this message means that something
a little more unusual is off in your test setup. Usually, this means that you're
specifying a Selenium version that isn't compatible with the browser/version/OS
you've selected. (For example, you should not be setting this for any mobile
tests.)

Try simply omitting that setting, and if you still see the issue, feel free to
contact our [support team](mailto:help@saucelabs.com) with a description of the
issue and a copy of your setup code.

##Client disconnected during getNewBrowserSession request

This means that your test runner decided to end the job before it had fully
initialized on Sauce's end. There are a few potential causes:
* You're running too many tests at a time: Check the left sidebar on your
  Account page (https://saucelabs.com/account). It shows a "Parallel tests"
  number, which is the maximum number of tests you can run at a time, based on
  your subscription level. If your account can run 2 parallel tests, and you're
  launching 10, 8 will be "queued" until one of your tests finishes and a slot
  frees up. However, if this takes a long time, your test runner may choose to
  end the queued jobs after a few minutes instead of waiting. Just make sure
  you're launching a reasonable number of simultaneous tests for your account.
* High job wait times: Check our status page (http://status.saucelabs.com/)
  and/or follow @sauceops on Twitter for up-to-the-minute news about any issues
  within the service. If something causes demand for certain VMs to stack up,
  your jobs may be queued and (as above) terminated by your test runner.

Tests that end this way are never taken out of your minutes.

##Runaway execution. Please contact help@saucelabs.com for assistance

This message means that an error has been detected in the VM or in Selenium,
which caused the test to behave abnormally, and Sauce detected this and shut
down the job. These are very rare and usually do not recur. If you do see more
than one on the same test, let us know.

##UnreachableBrowserException: Could not start a new session. Possible causes are invalid address of the remote server or browser start-up failure

This error is thrown on the client side, when Selenium isn't able to reach our
testing servers to start a session. Try running this on the command line:
```
telnet ondemand.saucelabs.com 80
```

If your machine is able to hit our servers, you should see:
```
Trying 65.50.202.179...
Connected to ondemand.saucelabs.com.
Escape character is '^]'.
```

If you do receive this successful message, and still can't start a session, send
that output to [our support team](mailto:help@saucelabs.com) and we'll troubleshoot
further.

Otherwise, there's definitely something blocking the connection on your end. If
so, you'll need to talk with your IT/network team to ensure that the required
ports can be made available.


text:
layout: default
title:  "Cake carottes citron"
categories:
- Sortie
- Invités
- Maison
- Dessert
tags:
- carotte
- farine
- amande
- sucre
- beurre
- oeuf
- citron
- levure
---

## Ingrédients

- 400g de carottes râpées
- 250g de farine
- 100g d'amande en poudre (ou farine)
- 250g de cassonade
- 125g de beurre ramolli
- 4 œufs
- 100g d'écorces confites de citron
- 1 sachet de levure chimique
- 1 pincée de sel

## Préparation

- Mélanger le beurre, le sucre roux.
- Ajouter les œufs un à un, une pincée de sel, la farine, les amandes et la levure
- Ajouter les carottes et les écorces de citron coupées en morceaux, ainsi qu'éventuellement quelques épices
- Beurrer un moule, verser dans le moule, enfourner

## Cuisson

- Enfourner pour ~ 40+ minutes à 220°