<?php

namespace Webapp\Core\Entity;

use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\ExistsFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Core\Serializer\Filter\GroupFilter;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity;
use Shared\Authentication\CustomFilters\DeletedFilter;
use Shared\Authentication\Entity\IdentifiedEntity;
use Shared\Authentication\Entity\Site;
use Shared\Authentication\Entity\User;
use Shared\Enumeration\Annotation\EnumType;
use Shared\RightManagement\Annotation\AdvancedRight;
use Shared\RightManagement\Traits\HasOwnerEntity;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Webapp\Core\Annotation\GraphicallyDeletable;
use Webapp\Core\ApiOperation\RestoreObjectOperation;
use Webapp\Core\Dto\BusinessObject\BusinessObjectCsvOutputDto;
use Webapp\Core\Entity\Attachment\PlatformAttachment;
use Webapp\Core\Enumeration\GraphicalOriginEnum;
use Webapp\Core\Traits\GraphicallyDeletableEntity;
use Webapp\Core\Validator\UniqueAttributeInParent;

/**
 * Class Platform
 * @package Webapp\Core\Entity
 *
 * @ApiResource(
 *     collectionOperations={
 *         "get"={},
 *         "post"={
 *              "security_post_denormalize"="is_granted('ROLE_PLATFORM_MANAGER', object.getSite())",
 *              "denormalization_context"={"groups"={"platform_post"}},
 *          },
 *     },
 *     itemOperations={
 *          "get"={},
 *          "patch"={
 *              "security"="is_granted('ROLE_PLATFORM_MANAGER', object.getSite())",
 *              "denormalization_context"={"groups"={"update_platform"}}
 *          },
 *          "delete"={
 *              "security"="is_granted('ROLE_PLATFORM_MANAGER', object.getSite()) && object.getProjects().count() === 0"
 *          },
 *          "restore"={
 *              "controller"=RestoreObjectOperation::class,
 *              "method"="PATCH",
 *              "path"="/platforms/{id}/restore",
 *              "security"="is_granted('ROLE_SITE_ADMIN')",
 *              "read"=false,
 *              "validate"=false,
 *              "openapi_context"={
 *                  "summary": "Restore deleted protocol",
 *                  "description": "Remove the deleted state"
 *              },
 *          },
 *          "export"={
 *              "method"="GET",
 *              "path"="/platforms/{id}/exportCsv",
 *              "formats"={"csv"={"text/csv"}},
 *              "pagination_enabled"=false,
 *              "output"=BusinessObjectCsvOutputDto::class
 *          }
 *     }
 * )
 * @ApiFilter(SearchFilter::class, properties={"site": "exact", "name": "exact", "projects": "exact", "experiments": "exact"})
 * @ApiFilter(ExistsFilter::class, properties={"deletedAt"})
 * @ApiFilter(GroupFilter::class, arguments={"whitelist"={
 *     "design_explorer_view",
 *     "platform_full_view",
 *     "admin_explorer_view",
 *     "platform_synthesis",
 *     "project_explorer_view"
 * }})
 * @ApiFilter(DeletedFilter::class)
 *
 * @AdvancedRight(classIdentifier="webapp_platform", ownerField="owner", siteAttribute="site")
 *
 * @Gedmo\SoftDeleteable()
 * @GraphicallyDeletable()
 *
 * @ORM\Entity()
 * @ORM\Table(name="platform", schema="webapp")
 */
class Platform extends IdentifiedEntity implements BusinessObject
{
    use HasOwnerEntity;

    use SoftDeleteableEntity;

    use GraphicallyDeletableEntity;

    /**
     * @var string
     *
     * @ORM\Column(type="string")
     * @Groups({"design_explorer_view", "update_platform", "platform_full_view", "project_explorer_view", "data_explorer_view", "webapp_data_view", "admin_explorer_view", "data_entry_synthesis", "platform_synthesis", "project_synthesis", "platform_post", "variable_synthesis"})
     * @Assert\NotBlank
     * @UniqueAttributeInParent(parentsAttributes={"site.platforms"})
     */
    private string $name;

    /**
     * @var string
     *
     * @ORM\Column(type="string")
     * @Groups({"platform_full_view", "update_platform", "platform_synthesis", "platform_post"})
     */
    private string $siteName;

    /**
     * @var string
     *
     * @ORM\Column(type="string")
     * @Groups({"platform_full_view", "update_platform", "platform_synthesis", "platform_post"})
     */
    private string $placeName;

    /**
     * @var string|null
     *
     * @ORM\Column(type="string", nullable=true)
     * @Groups({"platform_full_view", "update_platform", "platform_synthesis", "platform_post"})
     */
    private ?string $comment;

    /**
     * @var DateTime $created
     *
     * @Gedmo\Timestampable(on="create")
     * @ORM\Column(type="datetime")
     * @Groups({"platform_full_view", "platform_synthesis"})
     */
    private DateTime $created;

    /**
     * @var Collection|array
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\Experiment", mappedBy="platform", cascade={"persist", "remove"}, orphanRemoval=true)
     * @Groups({"design_explorer_view", "platform_full_view", "platform_synthesis", "platform_post"})
     */
    private $experiments;

    /**
     * @var Collection|array
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\Project", mappedBy="platform", cascade={"persist", "remove"})
     * @Groups({"design_explorer_view", "platform_synthesis", "project_explorer_view", "data_explorer_view"})
     */
    private $projects;

    /**
     * @var Site
     *
     * @Groups({"platform_synthesis", "change_report", "data_entry_synthesis", "project_synthesis", "platform_post", "variable_synthesis", "status_project_mobile_view"})
     * @ORM\ManyToOne(targetEntity="Shared\Authentication\Entity\Site", inversedBy="platforms")
     */
    private Site $site;

    /**
     * @var ?User the owner of the entity
     *
     * @ORM\ManyToOne(targetEntity="Shared\Authentication\Entity\User")
     * @Groups({"design_explorer_view", "platform_full_view", "platform_synthesis"})
     */
    private ?User $owner;

    /**
     * @var int|null
     * @ORM\Column(type="integer", nullable=true)
     * @Groups({"platform_full_view"})
     */
    private ?int $color;

    /**
     * @var int
     * @ORM\Column(type="integer", nullable=true)
     * @Groups({"platform_full_view", "update_platform"})
     */
    private int $xMesh;

    /**
     * @var int
     * @ORM\Column(type="integer", nullable=true)
     * @Groups({"platform_full_view", "update_platform"})
     */
    private int $yMesh;

    /**
     * @var int
     * @ORM\Column(type="integer", nullable=true)
     * @EnumType(class="Webapp\Core\Enumeration\GraphicalOriginEnum")
     * @Groups({"platform_full_view", "update_platform"})
     */
    private int $origin;

    /**
     * @var Collection<array-key, PlatformAttachment>|array<PlatformAttachment>
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\Attachment\PlatformAttachment", mappedBy="platform", cascade={"persist", "remove"})
     * @Groups({"platform_post"})
     */
    private $platformAttachments;

    /**
     * @var NorthIndicator|null
     * @ORM\OneToOne (targetEntity="Webapp\Core\Entity\NorthIndicator", mappedBy="platform", cascade={"persist", "remove"})
     * @Groups({"platform_full_view"})
     */
    private ?NorthIndicator $northIndicator;

    /**
     * @var Collection<array-key, GraphicalTextZone>|array<GraphicalTextZone>
     * @ORM\OneToMany (targetEntity="Webapp\Core\Entity\GraphicalTextZone", mappedBy="platform", cascade={"persist", "remove"})
     * @Groups({"platform_full_view"})
     */
    private $graphicalTextZones;

    public function __construct()
    {
        $this->experiments = new ArrayCollection();
        $this->projects = new ArrayCollection();
        $this->color = null;
        $this->xMesh = 50;
        $this->yMesh = 50;
        $this->origin = GraphicalOriginEnum::TOP_LEFT;
        $this->owner = null;
        $this->comment = null;
        $this->platformAttachments = new ArrayCollection();
        $this->graphicalTextZones = new ArrayCollection();
        $this->northIndicator = null;
    }

    /**
     * @Groups({"platform_full_view", "design_explorer_view"})
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @param string $name
     * @return Platform
     */
    public function setName(string $name): Platform
    {
        $this->name = $name;
        return $this;
    }

    /**
     * @return string
     */
    public function getSiteName(): string
    {
        return $this->siteName;
    }

    /**
     * @param string $siteName
     * @return Platform
     */
    public function setSiteName(string $siteName): Platform
    {
        $this->siteName = $siteName;
        return $this;
    }

    /**
     * @return string
     */
    public function getPlaceName(): string
    {
        return $this->placeName;
    }

    /**
     * @param string $placeName
     * @return Platform
     */
    public function setPlaceName(string $placeName): Platform
    {
        $this->placeName = $placeName;
        return $this;
    }

    /**
     * @return string|null
     */
    public function getComment(): ?string
    {
        return $this->comment;
    }

    /**
     * @param string|null $comment
     * @return Platform
     */
    public function setComment(?string $comment): Platform
    {
        $this->comment = $comment;
        return $this;
    }

    /**
     * @return DateTime
     */
    public function getCreated(): DateTime
    {
        return $this->created;
    }

    /**
     * @param DateTime $created
     * @return Platform
     */
    public function setCreated(DateTime $created): Platform
    {
        $this->created = $created;
        return $this;
    }

    /**
     * @return array<Experiment>|Collection
     */
    public function getExperiments()
    {
        return $this->experiments;
    }

    /**
     * @param array<Experiment>|Collection $experiments
     * @return Platform
     */
    public function setExperiments($experiments): Platform
    {
        $this->experiments = $experiments;
        foreach ($experiments as $experiment){
            $experiment->setPlatform($this);
        }
        return $this;
    }

    /**
     * @param Experiment $experiment
     * @return Platform
     */
    public function addExperiment(Experiment $experiment): Platform
    {
        if(!$this->experiments->contains($experiment)){
            $this->experiments->add($experiment);
            $experiment->setPlatform($this);
        }
        return $this;
    }

    /**
     * @param Experiment $experiment
     * @return Platform
     */
    public function removeExperiment(Experiment $experiment): Platform
    {
        if($this->experiments->contains($experiment)){
            $this->experiments->removeElement($experiment);
            $experiment->setPlatform(null);
        }
        return $this;
    }

    /**
     * @return Site
     */
    public function getSite(): Site
    {
        return $this->site;
    }

    /**
     * @param Site $site
     * @return Platform
     */
    public function setSite(Site $site): Platform
    {
        $this->site = $site;
        return $this;
    }

    /**
     * @return User|null
     */
    public function getOwner(): ?User
    {
        return $this->owner;
    }

    /**
     * @param User|null $owner
     * @return Platform
     */
    public function setOwner(?User $owner): Platform
    {
        $this->owner = $owner;
        return $this;
    }


    /**
     * @return array|Collection
     */
    public function getProjects()
    {
        return $this->projects;
    }

    /**
     * @param array|Collection $projects
     * @return Platform
     */
    public function setProjects($projects)
    {
        $this->projects = $projects;
        return $this;
    }

    /**
     * @param Project $project
     * @return Platform
     */
    public function addProject($project)
    {
        if (!$this->projects->contains($project)) {
            $this->projects->add($project);
            $project->setPlatform($this);
        }
        return $this;
    }

    /**
     * @param Project $project
     * @return Platform
     */
    public function removeProject($project)
    {
        if ($this->projects->contains($project)) {
            $this->projects->removeElement($project);
            $project->setPlatform(null);
        }
        return $this;
    }

    /**
     * @return int|null
     */
    public function getColor(): ?int
    {
        return $this->color;
    }

    /**
     * @param int|null $color
     * @return Platform
     */
    public function setColor(?int $color): Platform
    {
        $this->color = $color;
        return $this;
    }

    /**
     * @return int
     */
    public function getXMesh(): int
    {
        return $this->xMesh;
    }

    /**
     * @param int $xMesh
     * @return Platform
     */
    public function setXMesh(int $xMesh): Platform
    {
        $this->xMesh = $xMesh;
        return $this;
    }

    /**
     * @return int
     */
    public function getYMesh(): int
    {
        return $this->yMesh;
    }

    /**
     * @param int $yMesh
     * @return Platform
     */
    public function setYMesh(int $yMesh): Platform
    {
        $this->yMesh = $yMesh;
        return $this;
    }

    /**
     * @return int
     */
    public function getOrigin(): int
    {
        return $this->origin;
    }

    /**
     * @param int $origin
     * @return Platform
     */
    public function setOrigin(int $origin): Platform
    {
        $this->origin = $origin;
        return $this;
    }

    public function setDeletedAt(DateTime $deletedAt = null)
    {
        $this->deletedAt = $deletedAt;
        if($deletedAt === null){
            foreach ($this->children() as $child){
                $child->setDeletedAt($deletedAt);
            }
        }

        return $this;
    }

    /**
     * @return Collection|PlatformAttachment[]
     */
    public function getPlatformAttachments()
    {
        return $this->platformAttachments;
    }

    /**
     * @param Collection|PlatformAttachment[] $platformAttachments
     * @return Platform
     */
    public function setPlatformAttachments($platformAttachments)
    {
        $this->platformAttachments = $platformAttachments;
        return $this;
    }

    /**
     * @param PlatformAttachment $platformAttachment
     * @return Platform
     */
    public function addPlatformAttachment($platformAttachment)
    {
        if (!$this->platformAttachments->contains($platformAttachment)) {
            $this->platformAttachments->add($platformAttachment);
            $platformAttachment->setPlatform($this);
        }
        return $this;
    }

    /**
     * @param PlatformAttachment $platformAttachment
     * @return Platform
     */
    public function removePlatformAttachment($platformAttachment)
    {
        if ($this->platformAttachments->contains($platformAttachment)) {
            $this->platformAttachments->removeElement($platformAttachment);
            $platformAttachment->setPlatform(null);
        }
        return $this;
    }

    /**
     * @return NorthIndicator|null
     */
    public function getNorthIndicator(): ?NorthIndicator
    {
        return $this->northIndicator;
    }

    /**
     * @param NorthIndicator|null $northIndicator
     */
    public function setNorthIndicator(?NorthIndicator $northIndicator): void
    {
        $this->northIndicator = $northIndicator;
    }

    /**
     * @return Collection|GraphicalTextZone[]
     */
    public function getGraphicalTextZones()
    {
        return $this->graphicalTextZones;
    }

    /**
     * @param Collection|GraphicalTextZone[] $graphicalTextZones
     * @return Platform
     */
    public function setGraphicalTextZones($graphicalTextZones)
    {
        $this->graphicalTextZones = $graphicalTextZones;
        return $this;
    }

    /**
     * @param GraphicalTextZone $graphicalTextZone
     * @return Platform
     */
    public function addGraphicalTextZone($graphicalTextZone)
    {
        if (!$this->graphicalTextZones->contains($graphicalTextZone)) {
            $this->graphicalTextZones->add($graphicalTextZone);
            $graphicalTextZone->setPlatform($this);
        }
        return $this;
    }

    /**
     * @param GraphicalTextZone $graphicalTextZone
     * @return Platform
     */
    public function removeGraphicalTextZone($graphicalTextZone)
    {
        if ($this->graphicalTextZones->contains($graphicalTextZone)) {
            $this->graphicalTextZones->removeElement($graphicalTextZone);
            $graphicalTextZone->setPlatform(null);
        }
        return $this;
    }

    function parent(): ?BusinessObject
    {
        return null;
    }

    function children(): array
    {
        return [...$this->experiments->toArray()];
    }


}
