<?php

final class SemiStructuredObjectInstanceSearchEngine
  extends PhabricatorApplicationSearchEngine {

  private $objectType;

  public function setObjectType(SemiStructuredObjectType $object_type) {
    $this->objectType = $object_type;
    return $this;
  }

  public function getObjectType() {
    return $this->objectType;
  }

  public function getResultTypeDescription() {
    return pht('Object Instances');
  }

  public function getApplicationClassName() {
    return 'SemiStructuredDataApplication';
  }

  public function newQuery() {
    $query = id(new SemiStructuredObjectInstanceQuery());
    if ($this->getObjectType()) {
      $query->withObjectType($this->getObjectType());
    }
    return $query;
  }

  protected function buildCustomSearchFields() {
    $fields = array(
    );

    if ($this->getObjectType()) {
      $fields[] = id(new SemiStructuredStaticSearchField())
        ->setKey('objecttype')
        ->setLabel(pht('Object Type'))
        ->setValue($this->getObjectType()->getName());
      $fields[] = id(new SemiStructuredObjectTypePHIDSearchField())
        ->setKey('classPHIDs')
        ->setLabel(pht('Object Type PHID'))
        ->setValue($this->getObjectType()->getPHID());
    }


    return $fields;
  }

  protected function getURI($path) {
    if ($this->getObjectType()) {
      return "/semistruct/type/{$this->getObjectType()->getID()}/{$path}";
    }
    return "/semistruct/instance/{$path}";
  }

  public function getQueryResultsPageURI($query_key) {
    $this->loadObjectTypeFromQuery($query_key);
    return parent::getQueryResultsPageURI($query_key);
  }

  public function getCustomizeURI($query_key, $object_phid, $context_phid) {
    $this->loadObjectTypeFromQuery($query_key);
    return parent::getCustomizeURI($query_key, $object_phid, $context_phid);
  }

  public function isBuiltinQuery($query_key) {
    $this->loadObjectTypeFromQuery($query_key);

    $builtins = $this->getBuiltinQueries();
    $raw = $this->getRawQueryNames();
    return isset($builtins[$query_key]) || isset($raw[$query_key]);

  }

  public function getQueryManagementURI() {
    if ($this->getObjectType()) {
      return parent::getQueryManagementURI();
    }
    return '/semistruct/';
  }

  protected function getBuiltinQueryNames() {
    $raw_names = $this->getRawQueryNames();

    if (!$this->getObjectType()) {
      return $raw_names;
    }

    $names = array();
    $suffix = '-'.$this->getObjectType()->getId();

    foreach ($raw_names as $key => $value) {
      $names[$key.$suffix] = $value;
    }

    return $names;
  }

  private function getRawQueryNames() {
    $names = array();

    $names['all'] = pht('All Instances');

    return $names;
  }

  private function loadObjectTypeFromQuery($query_key) {
    $matches = null;
    if (preg_match('/^(\w+)-(\d+)$/', $query_key, $matches)) {
      $type_id = $matches[2];
      $query_key = $matches[1];

      if (!$this->getObjectType()) {
        $this->setObjectType(
          SemiStructuredObjectTypeQuery::loadOneById(
            $this->requireViewer(),
            $type_id));
      }
      return $matches[1];
    }

    return $query_key;
  }

  public function buildSavedQueryFromBuiltin($query_key) {
    $query = $this->newSavedQuery();
    $query->setQueryKey($query_key);

    $query_key = $this->loadObjectTypeFromQuery($query_key);

    switch ($query_key) {
      case 'all':
        return $query;
      case 'open':
        return $query->setParameter(
          'statuses',
          array(
            SemiStructuredObjectType::STATUS_ACTIVE,
          ));
    }

    return parent::buildSavedQueryFromBuiltin($query_key);
  }


  public function loadAllNamedQueries() {
    $named_queries = parent::loadAllNamedQueries();

    if (!$this->getObjectType()) {
      return $named_queries;
    }

    $target_class_phid = $this->getObjectType()->getPHID();

    $saved_queries = id(new PhabricatorSavedQueryQuery())
      ->setViewer($this->requireViewer())
      ->withQueryKeys(mpull($named_queries, 'getQueryKey'))
      ->execute();
    $saved_queries = mpull($saved_queries, 'getParameters', 'getQueryKey');

    foreach ($named_queries as $key => $named_query) {
      if ($named_query->getIsBuiltin()) {
        continue;
      }

      $class_phids = idxv($saved_queries, array($key, 'classPHIDs'));

      if (!$class_phids) {
        continue;
      }

      if (array_search($target_class_phid, $class_phids) === false) {
        unset($named_queries[$key]);
      }

    }

    return $named_queries;
  }

  protected function buildQueryFromParameters(array $map) {
    $query = $this->newQuery();

    // TODO type id/phid

    // if ($map['statuses']) {
    //   $query->withStatuses($map['statuses']);
    // }

    // if ($map['editable'] !== null) {
    //   $query->withCanEdit($map['editable']);
    // }

    return $query;
  }

  protected function getRequiredHandlePHIDsForResultList(
    array $objects,
    PhabricatorSavedQuery $query) {

    return array();
  }

  public function renderResultsDirectly(array $items) {
    return $this->renderResultList(
      $items,
      new PhabricatorSavedQuery(),
      array());
  }

  protected function renderResultList(
    array $items,
    PhabricatorSavedQuery $query ,
    array $handles) {

    $viewer = $this->requireViewer();

    if ($items) {
      $edge_query = id(new PhabricatorEdgeQuery())
        ->withSourcePHIDs(mpull($items, 'getPHID'))
        ->withEdgeTypes(
          array(
            PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
          ));

      $edge_query->execute();
    }

    $list = id(new PHUIObjectItemListView())
      ->setViewer($viewer);

    foreach ($items as $instance) {
      $item = id(new PHUIObjectItemView())
        ->setViewer($viewer)
        ->setHeader($instance->getName())
        ->setHref($instance->getURI())
        ->setObject($instance);

      $icon = id(new PHUIIconView())
        ->setIcon($instance->getIcon());
      $item->setImageIcon($icon);
      $item->setEpoch($instance->getDateModified());


      $phid = $instance->getPHID();
      $project_phids = $edge_query->getDestinationPHIDs(array($phid));
      $project_handles = $viewer->loadHandles($project_phids);

      $item->addAttribute(
        id(new PHUIHandleTagListView())
          ->setLimit(4)
          ->setNoDataString(pht('No Tags'))
          ->setSlim(true)
          ->setHandles($project_handles));

      $can_edit = PhabricatorPolicyFilter::hasCapability(
        $viewer,
        $instance,
        PhabricatorPolicyCapability::CAN_EDIT);

      $edit_uri = $this->getApplicationURI(
        "/editinstance/{$instance->getId()}/");
      $item->addAction((new PHUIListItemView())
          ->setName(pht('Edit'))
          ->setIcon('fa-edit')
          ->setDisabled(!$can_edit)
          ->setHref($edit_uri));

      $list->addItem($item);
    }

    $result = new PhabricatorApplicationSearchResultView();
    $result->setObjectList($list);
    $result->setNoDataString(pht('No objects found.'));

    return $result;
  }

/* -(  Export   )------------------------------------------------------------ */

  protected function newExportFields() {
    return array(
      id(new PhabricatorStringExportField())
        ->setKey('name')
        ->setLabel(pht('Name')),
      id(new PhabricatorStringExportField())
        ->setKey('description')
        ->setLabel(pht('Description')),
      id(new PhabricatorStringExportField())
        ->setKey('rawData')
        ->setLabel(pht('Raw Data')),
      id(new PhabricatorPHIDExportField())
        ->setKey('classPHID')
        ->setLabel(pht('Class PHID')),
      id(new PhabricatorURIExportField())
        ->setKey('uri')
        ->setLabel(pht('URI')),
    );
  }

  protected function newExportData(array $instances) {
    $export = array();
    foreach ($instances as $instance) {

      $export[] = array(
        'name' => $instance->getName(),
        'uri' => PhabricatorEnv::getProductionURI($instance->getURI()),
        'description' => $instance->getDescription(),
        'classPHID' => $instance->getClassPHID(),
        'rawData' => $instance->getRawData(),
      );
    }

    return $export;
  }

}
