using System;
using System.Collections.Generic;
using System.Linq;
using Manager.DTOs;
using ManagerService.Data;
using ManagerService.Data.SubSection;
using ManagerService.Security;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using NSwag.Annotations;
namespace ManagerService.Controllers
{
///
/// Les points d'intérêt d'une maquette 3D — item E7 du lot XR-4.
///
/// Pourquoi un contrôleur à part. Le CRUD des points existe déjà, mais il est
/// écrit sur SectionMap : OfType<SectionMap>(), MapPoints.
/// Une maquette a les mêmes points — c'est le même GeoPoint — sur une autre
/// collection. Élargir SectionMapController aurait mélangé deux types de
/// section dans chaque méthode ; ce contrôleur-ci ne parle que de maquettes.
///
/// La différence de fond tient en un champ : ici un point porte une
/// LocalTransform (x, y, z dans le repère du modèle) là où une carte porte
/// une géométrie PostGIS. Les deux coexistent sur l'entité, et rien n'oblige un point
/// à n'en avoir qu'une.
///
[Authorize(Policy = ManagerService.Service.Security.Policies.ContentEditor)]
[ApiController, Route("api/[controller]")]
[OpenApiTag("SectionScene3D", Description = "3D model section points of interest")]
public class SectionScene3DController : ControllerBase
{
private readonly ILogger _logger;
private readonly MyInfoMateDbContext _myInfoMateDbContext;
public SectionScene3DController(ILogger logger,
MyInfoMateDbContext myInfoMateDbContext)
{
_logger = logger;
_myInfoMateDbContext = myInfoMateDbContext;
}
/// Get all points of interest of a 3D model section
/// Section id
[AllowAnonymous]
[RequireAppKey]
[ProducesResponseType(typeof(List), 200)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
[HttpGet("{sectionId}/points")]
public ObjectResult GetAllPointsFromSection(string sectionId)
{
try
{
var section = _myInfoMateDbContext.Sections
.OfType()
.Include(s => s.Points)
.FirstOrDefault(s => s.Id == sectionId);
if (section == null)
throw new KeyNotFoundException("3D model section does not exist");
return new OkObjectResult(
(section.Points ?? new List()).Select(p => p.ToDTO()).ToList());
}
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
/// Add a point of interest to a 3D model section
/// Section id
/// Point to create
[ProducesResponseType(typeof(GeoPointDTO), 200)]
[ProducesResponseType(typeof(string), 400)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
[HttpPost("{sectionId}/points")]
public ObjectResult CreatePoint(string sectionId, [FromBody] GeoPointDTO geoPointDTO)
{
try
{
if (geoPointDTO == null)
throw new ArgumentNullException("GeoPoint is null");
var section = _myInfoMateDbContext.Sections
.OfType()
.Include(s => s.Points)
.FirstOrDefault(s => s.Id == sectionId);
if (section == null)
throw new KeyNotFoundException("3D model section does not exist");
var point = new GeoPoint
{
Title = geoPointDTO.title,
Description = geoPointDTO.description,
Contents = geoPointDTO.contents,
ImageResourceId = geoPointDTO.imageResourceId,
ImageUrl = geoPointDTO.imageUrl,
Schedules = geoPointDTO.schedules,
Prices = geoPointDTO.prices,
Phone = geoPointDTO.phone,
Email = geoPointDTO.email,
Site = geoPointDTO.site,
// Un point neuf n'a pas encore été posé sur la maquette : il arrive à
// l'origine du modèle, là où l'éditeur le montrera pour qu'on le place.
LocalTransform = geoPointDTO.localTransform ?? new Position3D()
};
section.Points ??= new List();
section.Points.Add(point);
_myInfoMateDbContext.SaveChanges();
return new OkObjectResult(point.ToDTO());
}
catch (ArgumentNullException ex)
{
return new BadRequestObjectResult(ex.Message) { };
}
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
/// Update a point of interest, position included
/// Point to update
[ProducesResponseType(typeof(GeoPointDTO), 200)]
[ProducesResponseType(typeof(string), 400)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
[HttpPut("points")]
public ObjectResult UpdatePoint([FromBody] GeoPointDTO geoPointDTO)
{
try
{
if (geoPointDTO == null)
throw new ArgumentNullException("GeoPoint param is null");
var point = _myInfoMateDbContext.GeoPoints
.FirstOrDefault(p => p.Id == geoPointDTO.id);
if (point == null)
throw new KeyNotFoundException("GeoPoint does not exist");
point.Title = geoPointDTO.title ?? point.Title;
point.Description = geoPointDTO.description ?? point.Description;
point.Contents = geoPointDTO.contents ?? point.Contents;
point.ImageResourceId = geoPointDTO.imageResourceId ?? point.ImageResourceId;
point.ImageUrl = geoPointDTO.imageUrl ?? point.ImageUrl;
// C'est le champ que l'éditeur 3D renvoie à chaque déplacement, et la
// seule raison pour laquelle cette route est appelée souvent.
if (geoPointDTO.localTransform != null)
point.LocalTransform = geoPointDTO.localTransform;
_myInfoMateDbContext.SaveChanges();
return new OkObjectResult(point.ToDTO());
}
catch (ArgumentNullException ex)
{
return new BadRequestObjectResult(ex.Message) { };
}
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
/// Delete a point of interest
/// Point id
[ProducesResponseType(typeof(string), 200)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
[HttpDelete("points/{id}")]
public ObjectResult DeletePoint(int id)
{
try
{
var point = _myInfoMateDbContext.GeoPoints.FirstOrDefault(p => p.Id == id);
if (point == null)
throw new KeyNotFoundException("GeoPoint does not exist");
_myInfoMateDbContext.GeoPoints.Remove(point);
_myInfoMateDbContext.SaveChanges();
return new OkObjectResult("Point deleted");
}
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
}
}