directive @isAuthorized on FIELD_DEFINITION
directive @isAdmin on FIELD_DEFINITION

scalar Time
scalar Any

enum OrderDirection {
  ASC
  DESC
}

input Pagination {
  limit: Int
  offset: Int
}

input Ordering {
  order_by: String
  order_direction: OrderDirection
}

"Credentials used to identify and authenticate a share token"
input ShareTokenCredentials {
  token: String!
  password: String
}

type Query {
  siteInfo: SiteInfo!

  "List of registered users, must be admin to call"
  user(order: Ordering, paginate: Pagination): [User!]! @isAdmin
  "Information about the currently logged in user"
  myUser: User! @isAuthorized

  myUserPreferences: UserPreferences! @isAuthorized

  "List of albums owned by the logged in user."
  myAlbums(
    order: Ordering,
    paginate: Pagination
    "Return only albums from the root directory of the user"
    onlyRoot: Boolean
    "Return also albums with no media directly in them"
    showEmpty: Boolean
    "Show only albums having favorites"
    onlyWithFavorites: Boolean
  ): [Album!]! @isAuthorized
  """
  Get album by id, user must own the album or be admin
  If valid tokenCredentials are provided, the album may be retrived without further authentication
  """
  album(id: ID!, tokenCredentials: ShareTokenCredentials): Album!

  "List of media owned by the logged in user"
  myMedia(order: Ordering, paginate: Pagination): [Media!]! @isAuthorized
  """
  Get media by id, user must own the media or be admin.
  If valid tokenCredentials are provided, the media may be retrived without further authentication
  """
  media(id: ID!, tokenCredentials: ShareTokenCredentials): Media!

  "Get a list of media by their ids, user must own the media or be admin"
  mediaList(ids: [ID!]!): [Media!]!

  myTimeline(paginate: Pagination, onlyFavorites: Boolean): [TimelineGroup!]! @isAuthorized

  "Get media owned by the logged in user, returned in GeoJson format"
  myMediaGeoJson: Any! @isAuthorized
  "Get the mapbox api token, returns null if mapbox is not enabled"
  mapboxToken: String

  shareToken(credentials: ShareTokenCredentials!): ShareToken!
  shareTokenValidatePassword(credentials: ShareTokenCredentials!): Boolean!

  search(query: String!, limitMedia: Int, limitAlbums: Int): SearchResult!

  myFaceGroups(paginate: Pagination): [FaceGroup!]! @isAuthorized
  faceGroup(id: ID!): FaceGroup! @isAuthorized
}

type Mutation {
  authorizeUser(username: String!, password: String!): AuthorizeResult!

  "Registers the initial user, can only be called if initialSetup from SiteInfo is true"
  initialSetupWizard(
    username: String!
    password: String!
    rootPath: String!
  ): AuthorizeResult

  "Scan all users for new media"
  scanAll: ScannerResult! @isAdmin
  "Scan a single user for new media"
  scanUser(userId: ID!): ScannerResult! @isAdmin

  "Generate share token for album"
  shareAlbum(albumId: ID!, expire: Time, password: String): ShareToken! @isAuthorized
  "Generate share token for media"
  shareMedia(mediaId: ID!, expire: Time, password: String): ShareToken! @isAuthorized
  "Delete a share token by it's token value"
  deleteShareToken(token: String!): ShareToken! @isAuthorized
  "Set a password for a token, if null is passed for the password argument, the password will be cleared"
  protectShareToken(token: String!, password: String): ShareToken! @isAuthorized

  "Mark or unmark a media as being a favorite"
  favoriteMedia(mediaId: ID!, favorite: Boolean!): Media! @isAuthorized

  updateUser(
    id: ID!
    username: String
    password: String
    admin: Boolean
  ): User! @isAdmin
  createUser(
    username: String!
    password: String
    admin: Boolean!
  ): User! @isAdmin
  deleteUser(id: ID!): User! @isAdmin

  "Add a root path from where to look for media for the given user"
  userAddRootPath(id: ID!, rootPath: String!): Album @isAdmin
  userRemoveRootAlbum(userId: ID!, albumId: ID!): Album @isAdmin

  """
  Set how often, in seconds, the server should automatically scan for new media,
  a value of 0 will disable periodic scans
  """
  setPeriodicScanInterval(interval: Int!): Int! @isAdmin

  "Set max number of concurrent scanner jobs running at once"
  setScannerConcurrentWorkers(workers: Int!): Int! @isAdmin

  changeUserPreferences(language: String): UserPreferences! @isAuthorized

  "Assign a label to a face group, set label to null to remove the current one"
  setFaceGroupLabel(faceGroupID: ID!, label: String): FaceGroup! @isAuthorized
  "Merge two face groups into a single one, all ImageFaces from source will be moved to destination"
  combineFaceGroups(destinationFaceGroupID: ID!, sourceFaceGroupID: ID!): FaceGroup! @isAuthorized
  "Move a list of ImageFaces to another face group"
  moveImageFaces(imageFaceIDs: [ID!]!, destinationFaceGroupID: ID!): FaceGroup! @isAuthorized
  "Check all unlabeled faces to see if they match a labeled FaceGroup, and move them if they match"
  recognizeUnlabeledFaces: [ImageFace!]! @isAuthorized
  "Move a list of ImageFaces to a new face group"
  detachImageFaces(imageFaceIDs: [ID!]!): FaceGroup! @isAuthorized
}

type Subscription {
  notification: Notification!
}

enum NotificationType {
  Message
  Progress
  "Close a notification with a given key"
  Close
}

type Notification {
  key: String!
  type: NotificationType!
  header: String!
  content: String!
  progress: Float
  positive: Boolean!
  negative: Boolean!
  "Time in milliseconds before the notification will close"
  timeout: Int
}

type AuthorizeResult {
  success: Boolean!
  status: String!
  token: String
}

type ScannerResult {
  finished: Boolean!
  success: Boolean!
  progress: Float
  message: String
}

"A token used to publicly access an album or media"
type ShareToken {
  id: ID!
  token: String!
  "The user who created the token"
  owner: User!
  "Optional expire date"
  expire: Time
  "Whether or not a password is needed to access the share"
  hasPassword: Boolean!

  "The album this token shares"
  album: Album
  "The media this token shares"
  media: Media
}

"General information about the site"
type SiteInfo {
  initialSetup: Boolean!
  "How often automatic scans should be initiated in seconds"
  periodicScanInterval: Int! @isAdmin
  "How many max concurrent scanner jobs that should run at once"
  concurrentWorkers: Int! @isAdmin
}

type User {
  id: ID!
  username: String!
  #albums: [Album]
  # rootPath: String! @isAdmin
  "All albums owned by this user"
  albums: [Album!]! @isAdmin
  "Top level albums owned by this user"
  rootAlbums: [Album!]! @isAdmin
  admin: Boolean!
  #shareTokens: [ShareToken]
}

enum LanguageTranslation {
  English,
  French,
  Italian,
  Swedish,
  Danish,
  Spanish,
  Polish,
  German,
}

type UserPreferences {
  id: ID!
  language: LanguageTranslation
}

type Album {
  id: ID!
  title: String!

  "The media inside this album"
  media(
    order: Ordering,
    paginate: Pagination
    "Return only the favorited media"
    onlyFavorites: Boolean
  ): [Media!]!

  "The albums contained in this album"
  subAlbums(
    order: Ordering,
    paginate: Pagination
  ): [Album!]!

  "The album witch contains this album"
  parentAlbum: Album
  "The user who owns this album"
  owner: User!
  "The path on the filesystem of the server, where this album is located"
  filePath: String!
  "An image in this album used for previewing this album"
  thumbnail: Media
  path: [Album!]!

  shares: [ShareToken!]!
}

type MediaURL {
  "URL for previewing the image"
  url: String!
  "Width of the image in pixels"
  width: Int!
  "Height of the image in pixels"
  height: Int!
  "The file size of the resource in bytes"
  fileSize: Int!
}

type MediaDownload {
  title: String!
  mediaUrl: MediaURL!
}

enum MediaType {
  Photo
  Video
}

type Media {
  id: ID!
  title: String!
  "Local filepath for the media"
  path: String!
  "URL to display the media in a smaller resolution"
  thumbnail: MediaURL
  "URL to display the photo in full resolution, will be null for videos"
  highRes: MediaURL
  "URL to get the video in a web format that can be played in the browser, will be null for photos"
  videoWeb: MediaURL
  "The album that holds the media"
  album: Album!
  exif: MediaEXIF
  videoMetadata: VideoMetadata
  favorite: Boolean!
  type: MediaType!

  shares: [ShareToken!]!
  downloads: [MediaDownload!]!

  faces: [ImageFace!]!
}

"EXIF metadata from the camera"
type MediaEXIF {
  id: ID!
  media: Media!
  "The model name of the camera"
  camera: String
  "The maker of the camera"
  maker: String
  "The name of the lens"
  lens: String
  dateShot: Time
  "The exposure time of the image"
  exposure: Float
  "The aperature stops of the image"
  aperture: Float
  "The ISO setting of the image"
  iso: Int
  "The focal length of the lens, when the image was taken"
  focalLength: Float
  "A formatted description of the flash settings, when the image was taken"
  flash: Int
  "An index describing the mode for adjusting the exposure of the image"
  exposureProgram: Int
}

type VideoMetadata {
  id: ID!
  media: Media!
  width: Int!
  height: Int!
  duration: Float!
  codec: String
  framerate: Float
  bitrate: String
  colorProfile: String
  audio: String
}

type SearchResult {
  query: String!
  albums: [Album!]!
  media: [Media!]!
}

type TimelineGroup {
  album: Album!
  media: [Media!]!
  mediaTotal: Int!
  date: Time!
}

type FaceGroup {
  id: ID!
  label: String
  imageFaces(paginate: Pagination): [ImageFace!]!
  imageFaceCount: Int!
}

type ImageFace {
  id: ID!
  media: Media!
  rectangle: FaceRectangle!
  faceGroup: FaceGroup!
}

type FaceRectangle {
  minX: Float!
  maxX: Float!
  minY: Float!
  maxY: Float!
}
