using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nuclex.Networking.Gallery3.Requests;
using System.Diagnostics;
using System.Net;
using System.Web.Script.Serialization;
namespace Nuclex.Networking.Gallery3 {
/// Entity, common base for albums and items
public class Entity : ConnectionUser {
///
/// Initializes a new gallery entity (which is either an album or an item)
///
///
/// Connection holding the resources needed to issue requests to
/// the gallery installation
///
///
/// The JSON data returned by the gallery REST API about this entity
///
internal Entity(IGalleryConnection connection, ref Responses.JsonItem jsonEntity) :
base(connection) {
this.jsonItem = jsonEntity;
}
/// The unique ID of the entity
///
/// Each album or item in gallery is assigned a unique ID by which it can be
/// queried or
///
public int Id {
get { return this.jsonItem.Entity.Id; }
}
/// Date and time at which this entity was added to the gallery
public DateTime CreationDate {
get { return DateTimeHelper.DateTimeFromGalleryTime(this.jsonItem.Entity.Created); }
}
/// Description assigned to this entity
public string Description {
get { return this.jsonItem.Entity.Description; }
}
/// Human-readable title assigned to this entity
public string Title {
get { return this.jsonItem.Entity.Title; }
}
/// String used to refer to this entity in an URL
public string Slug {
get { return this.jsonItem.Entity.Slug; }
}
/// Name of the entity in the host's file system
public string Name {
get { return this.jsonItem.Entity.Name; }
}
/// IDs of this entity's children
public int[] ChildIds {
get {
lock (this) {
extractChildIdsIfNotCached();
return this.childIds;
}
}
}
/// IDs of the tags associated with this entity
public int[] TagIDs {
get {
lock (this) {
extractTagIdsIfNotCached();
return this.tagIds;
}
}
}
/// Retrieves the direct children of this entity
/// The child entities
///
///
/// Children can be albums (if the entity is an album itself), comments or
/// items (in the form of photos and movies).
///
///
public Entity GetChild(int index) {
extractChildIdsIfNotCached();
if ((index < 0) || (index >= this.childIds.Length)) {
throw new ArgumentOutOfRangeException("index", "Child index out of range");
}
Responses.JsonItem jsonItem = RetrieveChildEntity(this.childIds[index]);
return constructEntityForItem(ref jsonItem);
}
/// Retrieves the direct children of this entity
/// The child entities
///
///
/// Warning: This call will query *all* children. For larger albums and
/// sensible use you should query the albums with the second overload of this
/// method so you can display a progress bar and let the user cancel at
/// various points in the query.
///
///
/// Children can be albums (if the entity is an album itself), comments or
/// items (in the form of photos and movies).
///
///
public Entity[] GetChildren() {
return getChildrenInternal(null, null);
}
/// Retrieves the direct children of this entity
/// Index of the entity at which querying will resume
/// Number of entities that will be queried
/// The queried entities
///
/// Children can be albums (if the entity is an album itself), comments or
/// items (in the form of photos and movies).
///
public Entity[] GetChildren(int start, int count) {
return getChildrenInternal(start, count);
}
/// Refreshes this entity's data
public virtual void Refresh() {
lock (this) {
this.jsonItem = RetrieveChildEntity(this.jsonItem.Entity.Id);
this.childIds = null;
this.tagIds = null;
}
}
/// Retrieves the JSON data of a single child entity
/// ID of the entity that will be queried
/// The JSON data of the queried entity
protected internal Responses.JsonItem RetrieveChildEntity(int id) {
string resource = "rest/item/" + id.ToString();
IRequest request = Connection.RequestFactory.CreateRequest(resource);
request.Method = "GET";
request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
request.AddHeader("X-Gallery-Request-Method", "get");
if (Connection.ApiKey != null) {
request.AddHeader("X-Gallery-Request-Key", Connection.ApiKey);
}
return request.GetJsonResponse();
}
/// Retrieves informations about the direct children of the entity
/// Index of the entity at which to begin
/// Number of entities that will be returned
/// The JSON data for the children of the entity
///
/// Less than 100 entities can be returned if the entity had less than
/// the specified number of children starting at the indicated index.
/// The maximum number of children that can be queried at once is 100.
///
protected internal Responses.JsonItem[] RetrieveChildren(int? start, int? count) {
#if MULTI_ITEM_QUERY_WORKS
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.Append("rest/items?url="); // Docs say 'urls', code says 'url'...
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
string json = serializer.Serialize(base.jsonItem.Members);
urlBuilder.Append(json); //HttpUtility.UrlEncode(json));
}
if(start.HasValue) {
urlBuilder.Append("&start=");
urlBuilder.Append(start.Value);
}
if(count.HasValue) {
urlBuilder.Append("&num=");
urlBuilder.Append(count.Value);
}
IRequest request = Connection.RequestFactory.CreateRequest(urlBuilder.ToString());
request.ContentType = "application/x-www-form-urlencoded";
request.AddHeader("X-Gallery-Request-Method", "get");
if (Connection.ApiKey != null) {
request.AddHeader("X-Gallery-Request-Key", Connection.ApiKey);
}
return request.GetJsonResponse();
#else
return retrieveChildEntitiesOneByOne(start, count);
#endif
}
///
/// Stupid workaround because I cannot get the multi-item query to work
///
/// Index of the entity at which to begin
/// Number of entities that will be returned
/// The JSON data for the children of the entity
///
/// Less than 100 entities can be returned if the entity had less than
/// the specified number of children starting at the indicated index.
/// The maximum number of children that can be queried for at once is 100.
///
private Responses.JsonItem[] retrieveChildEntitiesOneByOne(int? start, int? count) {
extractChildIdsIfNotCached();
// Determine the safe starting and ending indices
int begin = start ?? 0;
int end = this.childIds.Length;
if (count.HasValue) {
end = Math.Min(end, begin + count.Value);
}
end = Math.Min(end, begin + 100);
// Retrieve the children one by one... :-(
var items = new List(Math.Max(0, end - begin));
for (int index = begin; index < end; ++index) {
items.Add(RetrieveChildEntity(this.childIds[index]));
}
return items.ToArray();
}
///
/// Internal method used to obtain the direct children of this entity
///
/// Starting index if a subset is to be queried
/// Number of items that will be queried.
/// An array of children belonging to this entity
private Entity[] getChildrenInternal(int? start, int? count) {
Responses.JsonItem[] jsonEntities = RetrieveChildren(start, count);
List entities = new List(jsonEntities.Length);
for (int index = 0; index < jsonEntities.Length; ++index) {
entities.Add(constructEntityForItem(ref jsonEntities[index]));
}
return entities.ToArray();
}
/// Constructs an entity of the concrete type matching the JSON data
/// Item for which an entity will be constructed
/// The newly constructed entity
private Entity constructEntityForItem(ref Responses.JsonItem jsonItem) {
switch (jsonItem.Entity.Type) {
case "album": { return new Album(Connection, ref jsonItem); }
case "photo": { return new Photo(Connection, ref jsonItem); }
case "movie": { return new Movie(Connection, ref jsonItem); }
case "comment": { return new Comment(Connection, ref jsonItem); }
default: { return new Entity(Connection, ref jsonItem); }
}
}
///
/// Extracts the IDs of this entity's children if they're not cached yet
///
private void extractChildIdsIfNotCached() {
lock (this) {
if (this.childIds != null) {
return; // Already cached
}
this.childIds = UrlHelper.ExtractIdsFromUrls(this.jsonItem.Members);
}
}
///
/// Extracts the IDs of this entity's tags if they're not cached yet
///
private void extractTagIdsIfNotCached() {
lock (this) {
if (this.tagIds != null) {
return; // Already cached
}
this.tagIds = UrlHelper.ExtractIdsFromUrls(
this.jsonItem.Relationships.Tags.Members
);
}
}
/// The entity data received in JSON format
protected Responses.JsonItem jsonItem;
/// Contains the IDs of the entity's children
private int[] childIds;
/// Contains the IDs of the tags applied to the entity
private int[] tagIds;
}
} // namespace Nuclex.Networking.Gallery3