<?php

class GridGallery_Photos_Model_Position extends GridGallery_Core_BaseModel
{
  const SCOPE_FOLDER = 'folder';
  const SCOPE_GALLERY = 'gallery';
  const SCOPE_MAIN = 'main';

  /**
   * @var string
   */
  protected $table;

  /**
   * Constructor.
   */
  public function __construct($debugEnabled = false)
  {
    parent::__construct();

    $this->debugEnabled = (bool) $debugEnabled;
    $this->table = $this->db->prefix . 'gg_photos_pos';
  }

  /**
   * Updates the position
   * @param  array $data
   * @return bool
   */
  public function update(array $data)
  {
    if (!isset($data['elements']) || count($data['elements']) < 1) {
      return false;
    }

    $this->clearScope($data['scope'], $data['scope_id']);

    foreach ($data['elements'] as $element) {
      $this->updatePosition([
        'photo_id' => (int) $element['photo_id'],
        'position' => (int) $element['position'],
        'scope_id' => (int) $data['scope_id'],
        'scope' => $data['scope'],
      ]);
    }

    return true;
  }

  /**
   * Updates position for the single row.
   * @param  array $row
   * @return bool
   */
  public function updatePosition(array $row)
  {
    $query = $this->getQueryBuilder()->insertInto($this->table)->fields(array_keys($row))->values(array_values($row));

    if (!$this->db->query($query->build())) {
      return false;
    }

    return true;
  }

  /**
   * Clears the positions in the selected scope.
   * @param string $scope Scope type.
   * @param int $id Scope ID.
   */
  public function clearScope($scope = self::SCOPE_MAIN, $id = 0)
  {
    $query = $this->getQueryBuilder()->deleteFrom($this->table)->where('scope', '=', $scope)->andWhere('scope_id', '=', (int) $id);

    $this->db->query($query->build());
  }

  /**
   * Returns the positions of the element in the specific scope.
   * @param  int $id Element Id.
   * @param  string $scope Scope type.
   * @param  int $scopeId Scope Id.
   * @return int
   */
  public function getPosition($id, $scope = self::SCOPE_MAIN, $scopeId = 0)
  {
    $query = $this->getQueryBuilder()->select('position')->from($this->table)->where('scope', '=', $scope)->andWhere('scope_id', '=', (int) $scopeId)->andWhere('photo_id', '=', (int) $id);

    if (null === ($row = $this->db->get_row($query->build()))) {
      return 0;
    }

    return $row->position;
  }

  public function getPositions($scope = self::SCOPE_MAIN, $scopeId = 0)
  {
    $query = $this->getQueryBuilder()->select('position, photo_id')->from($this->table)->where('scope', '=', $scope)->andWhere('scope_id', '=', (int) $scopeId);

    if (null === ($row = $this->db->get_results($query->build()))) {
      return 0;
    }

    return $row;
  }

  /**
   * get the current position in scope
   * @param string $scope
   * @param int $scopeId
   * @return int current position in scope
   */
  public function getCurrentPosition($scope = self::SCOPE_MAIN, $scopeId = 0)
  {
    $query = $this->getQueryBuilder()->select('max(position) as position')->from($this->table)->where('scope', '=', $scope)->andWhere('scope_id', '=', (int) $scopeId);

    if (null === ($row = $this->db->get_row($query->build()))) {
      return 0;
    }

    return (int) $row->position + 1;
  }

  /**
   * Extends the photo object with the 'position' property.
   * @param  array|object $photo Photo object.
   * @param  string $scope Scope type.
   * @param  int $scopeId Scope Id.
   * @return array|object
   */
  public function setPosition($photo, $scope = self::SCOPE_MAIN, $scopeId = 0)
  {
    $isArray = false;

    if (is_array($photo)) {
      $photo = (object) $photo;
      $photos = $this->getPositions($scope, $scopeId);
      $isArray = true;

      return $photos;
    } else {
      $photo->position = $this->getPosition($photo->id, $scope, $scopeId);
    }

    return $isArray ? (array) $photo : $photo;
  }

  /**
   * Sorts an array of the photos by thier position.
   * @param  array $photos An array of the photos.
   * @return array
   */
  /**
   * Sorts an array of the photos by thier position.
   * @param  array $photos An array of the photos.
   * @return array
   */
  public function sort(array $photos, $sort = null)
  {
    $isObjectCollection = false;
    $position = [];
    $sorted = [];

    if (empty($photos)) {
      return [];
    }

    if ($sort == null) {
      $sort = [
        'sortby' => 'position',
        'sortto' => 'asc',
      ];
    }

    // If it is collection of the StdClass, them we are convert it to array.
    if (is_object($photos[0])) {
      $isObjectCollection = true;
      $photos = array_map([$this, 'toArray'], $photos);
    }

    // Sortto flag.
    if ($sort['sortto'] == 'asc' || empty($sort['sortto'])) {
      $sort_flag = SORT_ASC;
    } else {
      $sort_flag = SORT_DESC;
    }

    // If $photos is collection of the objects, then conver rows to the objs.
    if ($isObjectCollection) {
      return array_map([$this, 'toObject'], $this->sortBy($photos, $sort['sortby'], $sort_flag));
    }

    // ... or simply return array
    return $photos;
  }

  /**
   * Sorted by photos
   * @param array $photos
   * @param string $sortby Tag when be sorted ('title','position'...)
   * @param flag $flag Sorted flag (ASC, DESC)
   * @return array $photos
   */
  private function sortBy(array $photos, $sortby, $flag)
  {
    switch ($sortby) {
      //По дате добавления
      case 'adate':
        $date = [];
        foreach ($photos as $key => $row) {
          $date[$key] = $row['timestamp'];
        }
        array_multisort($date, $flag, $photos);
        break;

      //По дате создания
      case 'date':
        $date = [];
        foreach ($photos as $key => $row) {
          $date[$key] = $row['attachment']['date'];
        }
        array_multisort($date, $flag, $photos);
        break;

      //По размеру
      case 'size':
        $date = [];
        foreach ($photos as $key => $row) {
          $date[$key] = $row['attachment']['filesizeInBytes'];
        }
        array_multisort($date, $flag, $photos);
        break;

      //По имени
      case 'name':
        $title = [];
        foreach ($photos as $key => $row) {
          $title[$key] = $row['attachment']['name'];
        }
        array_multisort($title, $flag, $photos);
        break;

      //По имени файла
      case 'filename':
        $filenames = [];
        foreach ($photos as $key => $row) {
          $filenames[$key] = $row['attachment']['filename'];
        }
        array_multisort($filenames, $flag, SORT_NATURAL | SORT_FLAG_CASE, $photos);
        break;

      //По тегам
      case 'tags':
        $date = [];
        foreach ($photos as $key => $row) {
          if (isset($row['tags'])) {
            $date[$key] = $row['tags'];
          } else {
            $date[$key] = $row['position'];
          }
        }
        array_multisort($date, $flag, $photos);
        break;

      // В случайном порядке
      case 'randomly':
        $countPhoto = count($photos);
        for ($ind1 = 0; $ind1 < $countPhoto; $ind1++) {
          $rndSorArr[] = rand() % 500;
        }
        array_multisort($rndSorArr, $photos);
        break;

      //По позиции
      default:
        $position = [];
        foreach ($photos as $key => $row) {
          $position[$key] = $row['position'];
        }
        array_multisort($position, $flag, $photos);
        break;
    }

    return $photos;
  }

  /**
   * Casts the element to array.
   * @param  object $element
   * @return array
   */
  public function toArray($element)
  {
    return (array) $element;
  }

  /**
   * Casts the element to object
   * @param  array $element
   * @return object
   */
  public function toObject($element)
  {
    return (object) $element;
  }

  public function getRowsByScopeId($id)
  {
    $query = $this->getQueryBuilder()->select('photo_id', 'scope', 'position')->from($this->table)->where('scope_id', '=', (int) $id);
    $res = $this->db->get_results($query->build());
    return $res;
  }

  public function cloneByScopeId($oldId, $newId)
  {
    $scopes = $this->getRowsByScopeId($oldId);
    if (count($scopes)) {
      $query = $this->getQueryBuilder()->insertInto($this->table)->fields('photo_id', 'scope', 'scope_id', 'position');
      foreach ($scopes as $rowValue) {
        $query = $query->values($rowValue->photo_id, $rowValue->scope, $newId, $rowValue->position);
      }
      $this->db->query($query);
    }
    return true;
  }

  public function insertPosition(array $rowArr, $scope_id, $scope)
  {
    $query = $this->getQueryBuilder()->insertInto($this->table)->fields('photo_id', 'position', 'scope_id', 'scope');

    if (count($rowArr)) {
      foreach ($rowArr as $keyRow => $valueRow) {
        $query = $query->values($valueRow['photo_id'], $valueRow['position'], $scope_id, $scope);
      }
    }

    if (!$this->db->query($query->build())) {
      return false;
    }

    return true;
  }

  public function replacePosition(array $data)
  {
    if (!isset($data['elements']) || count($data['elements']) < 1) {
      return false;
    }

    $removeParams = [];
    $insertParams = [];
    foreach ($data['elements'] as $element) {
      $insertParams[] = [
        'photo_id' => (int) $element['photo_id'],
        'position' => (int) $element['position'],
      ];
      $removeParams[] = (int) $element['photo_id'];
    }

    $this->removeByPhotoIds($data['scope_id'], $removeParams, $data['scope']);
    $this->insertPosition($insertParams, (int) $data['scope_id'], $data['scope']);

    return true;
  }

  public function removeByPhotoIds($scope_id, array $photoIds, $scope = self::SCOPE_MAIN)
  {
    $query = $this->getQueryBuilder()->deleteFrom($this->table)->where('scope', '=', $scope)->andWhere('scope_id', '=', $scope_id)->andWhere('photo_id', 'IN', $photoIds);

    $this->db->query($query->build());
  }

  public function cleanAndCreate($galleryId, $photoRows, $sortTo = 1)
  {
    $this->clearScope('gallery', $galleryId);
    $position = 0;
    $count = count($photoRows);
    if ($count) {
      foreach ($photoRows as $element) {
        $insertParams[] = [
          'photo_id' => (int) $element['resource_id'],
          'position' => $sortTo == 1 ? $position : $count - $position - 1,
        ];
        $position++;
      }
      $this->insertPosition($insertParams, (int) $galleryId, 'gallery');
    }
  }
}
