// This file was generated by the Gtk# code generator.
// Any changes made will be lost if regenerated.

namespace Gst.Net {

	using System;
	using System.Collections;
	using System.Collections.Generic;
	using System.Runtime.InteropServices;

#region Autogenerated code
	public partial class NetTimeProvider : Gst.Object, GLib.IInitable {

		public NetTimeProvider (IntPtr raw) : base(raw) {}

		[DllImport("gstnet-1.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
		static extern IntPtr gst_net_time_provider_new(IntPtr clock, IntPtr address, int port);

		public NetTimeProvider (Gst.Clock clock, string address, int port) : base (IntPtr.Zero)
		{
			if (GetType () != typeof (NetTimeProvider)) {
				var vals = new List<GLib.Value> ();
				var names = new List<string> ();
				if (clock != null) {
					names.Add ("clock");
					vals.Add (new GLib.Value (clock));
				}
				names.Add ("address");
				vals.Add (new GLib.Value (address));
				names.Add ("port");
				vals.Add (new GLib.Value (port));
				CreateNativeObject (names.ToArray (), vals.ToArray ());
				return;
			}
			IntPtr native_address = GLib.Marshaller.StringToPtrGStrdup (address);
			Raw = gst_net_time_provider_new(clock == null ? IntPtr.Zero : clock.Handle, native_address, port);
			GLib.Marshaller.Free (native_address);
		}

		[GLib.Property ("active")]
		public bool Active {
			get {
				GLib.Value val = GetProperty ("active");
				bool ret = (bool) val;
				val.Dispose ();
				return ret;
			}
			set {
				GLib.Value val = new GLib.Value(value);
				SetProperty("active", val);
				val.Dispose ();
			}
		}

		[GLib.Property ("address")]
		public string Address {
			get {
				GLib.Value val = GetProperty ("address");
				string ret = (string) val;
				val.Dispose ();
				return ret;
			}
		}

		[GLib.Property ("clock")]
		public Gst.Clock Clock {
			get {
				GLib.Value val = GetProperty ("clock");
				Gst.Clock ret = (Gst.Clock) val;
				val.Dispose ();
				return ret;
			}
		}

		[GLib.Property ("port")]
		public int Port {
			get {
				GLib.Value val = GetProperty ("port");
				int ret = (int) val;
				val.Dispose ();
				return ret;
			}
		}

		[GLib.Property ("qos-dscp")]
		public int QosDscp {
			get {
				GLib.Value val = GetProperty ("qos-dscp");
				int ret = (int) val;
				val.Dispose ();
				return ret;
			}
			set {
				GLib.Value val = new GLib.Value(value);
				SetProperty("qos-dscp", val);
				val.Dispose ();
			}
		}


		// Internal representation of the wrapped structure ABI.
		static GLib.AbiStruct _class_abi = null;
		static public new GLib.AbiStruct class_abi {
			get {
				if (_class_abi == null)
					_class_abi = new GLib.AbiStruct (new List<GLib.AbiField>{ 
						new GLib.AbiField("_gst_reserved"
							, Gst.Object.class_abi.Fields
							, (uint) Marshal.SizeOf(typeof(IntPtr)) * 4 // _gst_reserved
							, null
							, null
							, (uint) Marshal.SizeOf(typeof(IntPtr))
							, 0
							),
					});

				return _class_abi;
			}
		}


		// End of the ABI representation.

		[DllImport("gstnet-1.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
		static extern IntPtr gst_net_time_provider_get_type();

		public static new GLib.GType GType { 
			get {
				IntPtr raw_ret = gst_net_time_provider_get_type();
				GLib.GType ret = new GLib.GType(raw_ret);
				return ret;
			}
		}

		[DllImport("gio-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
		static extern bool g_initable_init(IntPtr raw, IntPtr cancellable, out IntPtr error);

		public bool Init(GLib.Cancellable cancellable) {
			IntPtr error = IntPtr.Zero;
			bool raw_ret = g_initable_init(Handle, cancellable == null ? IntPtr.Zero : cancellable.Handle, out error);
			bool ret = raw_ret;
			if (error != IntPtr.Zero) throw new GLib.GException (error);
			return ret;
		}


		static NetTimeProvider ()
		{
			GtkSharp.GstreamerSharp.ObjectManager.Initialize ();
		}

		// Internal representation of the wrapped structure ABI.
		static GLib.AbiStruct _abi_info = null;
		static public new GLib.AbiStruct abi_info {
			get {
				if (_abi_info == null)
					_abi_info = new GLib.AbiStruct (new List<GLib.AbiField>{ 
						new GLib.AbiField("priv"
							, Gst.Object.abi_info.Fields
							, (uint) Marshal.SizeOf(typeof(IntPtr)) // priv
							, null
							, "_gst_reserved"
							, (uint) Marshal.SizeOf(typeof(IntPtr))
							, 0
							),
						new GLib.AbiField("_gst_reserved"
							, -1
							, (uint) Marshal.SizeOf(typeof(IntPtr)) * 4 // _gst_reserved
							, "priv"
							, null
							, (uint) Marshal.SizeOf(typeof(IntPtr))
							, 0
							),
					});

				return _abi_info;
			}
		}


		// End of the ABI representation.

#endregion
	}
}
