﻿//-----------------------------------------------------------------------
// <copyright file="VectorTile.cs" company="Mapbox">
//     Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Mapbox.Map
{
	using System.Collections.ObjectModel;
	using Mapbox.Utils;
	using Mapbox.VectorTile;
	using Mapbox.VectorTile.ExtensionMethods;
	using System;

	/// <summary>
	///    A decoded vector tile, as specified by the
	///    <see href="https://www.mapbox.com/vector-tiles/specification/">
	///    Mapbox Vector Tile specification</see>.
	///    See available layers and features <see href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/">here</see>.
	///    The tile might be incomplete if the network request and parsing are still pending.
	/// </summary>
	///  <example>
	/// Making a VectorTile request:
	/// <code>
	/// var parameters = new Tile.Parameters();
	/// parameters.Fs = MapboxAccess.Instance;
	/// parameters.Id = new CanonicalTileId(_zoom, _tileCoorindateX, _tileCoordinateY);
	/// parameters.TilesetId = "mapbox.mapbox-streets-v7";
	/// var vectorTile = new VectorTile();
	///
	/// // Make the request.
	/// vectorTile.Initialize(parameters, (Action)(() =>
	/// {
	/// 	if (!string.IsNullOrEmpty(vectorTile.Error))
	/// 	{
	///			// Handle the error.
	///		}
	///
	/// 	// Consume the <see cref="Data"/>.
	///	}));
	/// </code>
	/// </example>
	public sealed class VectorTile : Tile, IDisposable
	{
		// FIXME: Namespace here is very confusing and conflicts (sematically)
		// with his class. Something has to be renamed here.
		private Mapbox.VectorTile.VectorTile data;

		bool _isStyleOptimized = false;

		string _optimizedStyleId;

		string _modifiedDate;

		private bool isDisposed = false;

		/// <summary> Gets the vector decoded using Mapbox.VectorTile library. </summary>
		/// <value> The GeoJson data. </value>
		public Mapbox.VectorTile.VectorTile Data
		{
			get
			{
				return this.data;
			}
		}

		public VectorTile()
		{
			_isStyleOptimized = false;
		}

		public VectorTile(string styleId, string modifiedDate)
		{
			if (string.IsNullOrEmpty(styleId) || string.IsNullOrEmpty(modifiedDate))
			{
				UnityEngine.Debug.LogWarning("Style Id or Modified Time cannot be empty for style optimized tilesets. Switching to regular tilesets!");
				_isStyleOptimized = false;
			}
			else
			{
				_isStyleOptimized = true;
				_optimizedStyleId = styleId;
				_modifiedDate = modifiedDate;
			}
		}

		//TODO: uncomment if 'VectorTile' class changes from 'sealed'
		//protected override void Dispose(bool disposeManagedResources)
		//~VectorTile()
		//{
		//    Dispose(false);
		//}


		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		//TODO: change signature if 'VectorTile' class changes from 'sealed'
		//protected override void Dispose(bool disposeManagedResources)
		public void Dispose(bool disposeManagedResources)
		{
			if (!isDisposed)
			{
				if (disposeManagedResources)
				{
					//TODO implement IDisposable with Mapbox.VectorTile.VectorTile
					if (null != data)
					{
						data = null;
					}
				}
			}
		}


		/// <summary>
		/// <para>Gets the vector in a GeoJson format.</para>
		/// <para>
		/// This method should be avoided as it fully decodes the whole tile and might pose performance and memory bottle necks.
		/// </para>
		/// </summary>
		/// <value> The GeoJson data. </value>
		/// <example>
		/// Inspect the GeoJson.
		/// <code>
		/// var json = VectorTile.GeoJson;
		/// Console.Write("GeoJson: " + json);
		/// </code>
		/// </example>
		public string GeoJson
		{
			get
			{
				return this.data.ToGeoJson((ulong)Id.Z, (ulong)Id.X, (ulong)Id.Y, 0);
			}
		}


		/// <summary>
		/// Gets all availble layer names.
		/// See available layers and features <see href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/">here</see>.
		/// </summary>
		/// <returns>Collection of availble layers.</returns>
		/// <example>
		/// Inspect the LayerNames.
		/// <code>
		/// var layerNames = vectorTile.LayerNames();
		/// foreach (var layer in layerNames)
		/// {
		/// 	Console.Write("Layer: " + layer);
		/// }
		/// </code>
		/// </example>
		public ReadOnlyCollection<string> LayerNames()
		{
			return this.data.LayerNames();
		}

		// FIXME: Why don't these work?
		/// <summary>
		/// Decodes the requested layer.
		/// </summary>
		/// <param name="layerName">Name of the layer to decode.</param>
		/// <returns>Decoded VectorTileLayer or 'null' if an invalid layer name was specified.</returns>
		/// <example>
		/// Inspect a layer of the vector tile.
		/// <code>
		/// var countryLabelLayer = vectorTile.GetLayer("country_label");
		/// var count = countryLabelLayer.Keys.Count;
		/// for (int i = 0; i &lt; count; i++)
		/// {
		/// 	Console.Write(string.Format("{0}:{1}", countryLabelLayer.Keys[i], countryLabelLayer.Values[i]));
		/// }
		/// </code>
		/// </example>
		public VectorTileLayer GetLayer(string layerName)
		{
			return this.data.GetLayer(layerName);
		}


		internal override TileResource MakeTileResource(string tilesetId)
		{

			return (_isStyleOptimized) ?
				TileResource.MakeStyleOptimizedVector(Id, tilesetId, _optimizedStyleId, _modifiedDate)
			  : TileResource.MakeVector(Id, tilesetId);
		}


		internal override bool ParseTileData(byte[] data)
		{
			try
			{
				var decompressed = Compression.Decompress(data);
				this.data = new Mapbox.VectorTile.VectorTile(decompressed);
				return true;
			}
			catch (Exception ex)
			{
				AddException(ex);
				return false;
			}
		}


	}
}
