<?php

final class SemiStructuredObjectInstance extends SemiStructuredDAO
  implements
    PhabricatorPolicyInterface,
    PhabricatorApplicationTransactionInterface,
    PhabricatorSubscribableInterface,
    PhabricatorFlaggableInterface,
    PhabricatorProjectInterface,
    PhabricatorFerretInterface,
    PhabricatorFulltextInterface,
    PhabricatorMentionableInterface,
    PhabricatorConduitResultInterface,
    PhabricatorCustomFieldInterface,
    PhabricatorDestructibleInterface {


  protected $classPHID;
  protected $status;
  protected $name;
  protected $rawData = '{}';
  protected $description;


  const STATUS_ACTIVE = 'active';
  const STATUS_ARCHIVED = 'archived';

  private $class = self::ATTACHABLE;
  private $customFields = self::ATTACHABLE;

  public static function initializeNewObjectInstance(
      PhabricatorUser $actor,
      ?SemiStructuredObjectType $object_class = null) {

    $object = id(new self())
    // TODO take policies from class?
    // ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
    // ->setEditPolicy($actor->getPHID())
    ->setStatus(self::STATUS_ACTIVE);

    if ($object_class !== null) {
      $object
        ->setClassPHID($object_class->getPHID())
        ->attachClass($object_class);
    }

    return $object;
  }

  public function attachClass(
      ?SemiStructuredObjectType $object_class = null) {
    $this->class = $object_class;
    return $this;
  }

  public function getClass() {
    return $this->assertAttached($this->class);
  }

  public function hasAttachedClass() {
    return $this->class !== self::ATTACHABLE;
  }

  public function getURI() {
    return urisprintf('/semistruct/instance/%d/', $this->getID());
  }


  public function getIcon() {
    // TODO customize in Type
    return SemiStructuredDataApplication::ICON_OBJECT_INSTANCE;
  }

  public static function getStatusNameMap() {
    return array(
      // TODO remove this
      self::STATUS_ACTIVE => pht('Active'),
      self::STATUS_ARCHIVED => pht('Archived'),
    );
  }

  protected function getConfiguration() {
    return array(
      self::CONFIG_AUX_PHID => true,
      self::CONFIG_SERIALIZATION => array(
        'rawData' => self::SERIALIZATION_JSON,
      ),
      self::CONFIG_COLUMN_SCHEMA => array(
        'name' => 'sort255',
        'description' => 'text',
        'status' => 'text32',
      ),
      self::CONFIG_KEY_SCHEMA => array(
        'classPHID' => array(
          'columns' => array('classPHID'),
        ),
      ),
    ) + parent::getConfiguration();
  }


  public function getPHIDType() {
    return SemiStructuredObjectInstancePHIDType::TYPECONST;
  }

/* -(  PhabricatorApplicationTransactionInterface  )------------------------- */


  public function getApplicationTransactionEditor() {
    return new SemiStructuredObjectInstanceTransactionEditor();
  }

  public function getApplicationTransactionTemplate() {
    return new SemiStructuredObjectInstanceTransaction();
  }


/* -(  PhabricatorPolicyInterface  )----------------------------------------- */


  public function getCapabilities() {
    return array(
      PhabricatorPolicyCapability::CAN_VIEW,
      PhabricatorPolicyCapability::CAN_EDIT,
    );
  }

  public function getPolicy($capability) {
return ''; // TODO
    switch ($capability) {
      // TODO
      case PhabricatorPolicyCapability::CAN_VIEW:
        return $this->getViewPolicy();
      case PhabricatorPolicyCapability::CAN_EDIT:
        return $this->getEditPolicy();
    }
  }

  public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
    return true;
  }

/* -(  PhabricatorCustomFieldInterface  )------------------------------------ */


  public function getCustomFieldSpecificationForRole($role) {
    return array();
  }

  public function getCustomFieldBaseClass() {
    return 'SemiStructuredInstanceCustomField';
  }

  public function getCustomFields() {
    return $this->assertAttached($this->customFields);
  }

  public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
    $this->customFields = $fields;
    return $this;
  }


/* -(  PhabricatorConduitResultInterface  )---------------------------------- */


  public function getFieldSpecificationsForConduit() {
    return array(
      (new PhabricatorConduitSearchFieldSpecification())
        ->setKey('name')
        ->setType('string')
        ->setDescription(pht('The name of the instance.')),
      (new PhabricatorConduitSearchFieldSpecification())
        ->setKey('description')
        ->setType('remarkup')
        ->setDescription(pht('The description.')),
      (new PhabricatorConduitSearchFieldSpecification())
        ->setKey('classPHID')
        ->setType('phid')
        ->setDescription(pht('The Type of this Instance.')),
      (new PhabricatorConduitSearchFieldSpecification())
        ->setKey('rawData')
        ->setType('map<string, wild>')
        ->setDescription(pht('The raw structured data')),

// TODO custom fields

    );
  }

  public function getFieldValuesForConduit() {
    return array(
      'name' => $this->getName(),
      'description' => array(
        'raw' => $this->getDescription(),
      ),
      'classPHID' => $this->getClassPHID(),
      'rawData' => json_decode($this->getRawData()),
    );
  }

  public function getConduitSearchAttachments() {
    return array();
  }

/* -(  PhabricatorFerretInterface  )----------------------------------------- */


  public function newFerretEngine() {
    return new SemiStructuredObjectInstanceFerretEngine();
  }

/* -(  PhabricatorFulltextInterface  )--------------------------------------- */


  public function newFulltextEngine() {
    return new SemiStructuredObjectInstanceFulltextEngine();
  }


/* -(  PhabricatorDestructibleInterface  )----------------------------------- */


  public function destroyObjectPermanently(
    PhabricatorDestructionEngine $engine) {

    $this->openTransaction();
      $this->delete();
    $this->saveTransaction();
  }


/* -(  PhabricatorSubscribableInterface  )----------------------------------- */


  public function isAutomaticallySubscribed($phid) {
    return false;
  }

}
