<?php

class Live_Api_Clash extends Core_Api_Abstract
{
  public function isEnabled()
  {
    return (bool) Engine_Api::_()->getApi('settings', 'core')->getSetting('live.allow.clash', 1);
  }

  public function requestClash($streamA, $streamB, $viewer)
  {
    if (!$this->isEnabled()) {
      throw new Engine_Exception('Live Clash is disabled.');
    }

    if (!$viewer || !$viewer->getIdentity()) {
      throw new Engine_Exception('Login required.');
    }

    $streamA = $this->_normalizeStreamForClash($streamA);
    $streamB = $this->_normalizeStreamForClash($streamB);

    if (!$streamA || !$streamB) {
      throw new Engine_Exception('Both live streams are required.');
    }

    if ((int) $streamA->host_id != (int) $viewer->getIdentity()) {
      throw new Engine_Exception('Only the host can request a Live Clash.');
    }

    if (!$streamA->allow_clash || !$streamB->allow_clash) {
      throw new Engine_Exception('Live Clash is not allowed for one of these streams.');
    }

    if ((int) $streamA->getIdentity() === (int) $streamB->getIdentity() || (int) $streamA->host_id === (int) $streamB->host_id) {
      throw new Engine_Exception('A creator cannot start a Live Clash with the same live or account.');
    }

    if ($streamA->status != Live_Api_Core::STATUS_LIVE || $streamB->status != Live_Api_Core::STATUS_LIVE) {
      throw new Engine_Exception('Both creators must be live before starting a Live Clash.');
    }

    $settings = Engine_Api::_()->getApi('settings', 'core');
    Engine_Api::_()->live()->assertRateLimit('clash_request', 0, (int) $settings->getSetting('live.rate.clash.request.hour', 20), 3600);

    $table = Engine_Api::_()->getDbtable('clashes', 'live');
    $participantTable = Engine_Api::_()->getDbtable('clashParticipants', 'live');
    $db = $table->getAdapter();
    $streamIds = array((int) $streamA->getIdentity(), (int) $streamB->getIdentity());
    $lockNames = $this->_acquireClashLocks($db, $streamIds);
    $transactionStarted = false;

    try {
      $existing = $this->getClashBetweenStreams($streamA, $streamB, array('pending', 'accepted', 'live'));
      if ($existing) {
        return $existing;
      }

      $conflict = $this->_getClashForAnyStream($streamIds, array('pending', 'accepted', 'live'));
      if ($conflict) {
        throw new Engine_Exception('One of these creators already has a pending or active Live Clash.');
      }

      $db->beginTransaction();
      $transactionStarted = true;
      $clash = $table->createRow();
      $clash->status = 'pending';
      $clash->stream_id_a = $streamA->getIdentity();
      $clash->stream_id_b = $streamB->getIdentity();
      $clash->score_a = 0;
      $clash->score_b = 0;
      $clash->creation_date = date('Y-m-d H:i:s');
      $clash->save();

      $this->_createParticipant($participantTable, $clash, $streamA, 'a');
      $this->_createParticipant($participantTable, $clash, $streamB, 'b');

      $db->commit();
      $transactionStarted = false;
      return $clash;
    } catch (Exception $e) {
      if ($transactionStarted) {
        $db->rollBack();
      }
      throw $e;
    } finally {
      $this->_releaseClashLocks($db, $lockNames);
    }
  }

  public function acceptClash($clash, $viewer)
  {
    if (!$clash) {
      throw new Engine_Exception('Live Clash not found.');
    }

    if ($clash->status != 'pending') {
      return $clash;
    }

    $streamA = Engine_Api::_()->getItem('live_stream', $clash->stream_id_a);
    $streamB = Engine_Api::_()->getItem('live_stream', $clash->stream_id_b);
    if (!$viewer || !$streamB || (int) $streamB->host_id != (int) $viewer->getIdentity()) {
      throw new Engine_Exception('Only the invited host can accept this Live Clash.');
    }

    if (!$streamA || $streamA->status != Live_Api_Core::STATUS_LIVE || $streamB->status != Live_Api_Core::STATUS_LIVE) {
      throw new Engine_Exception('Both creators must still be live to accept this clash.');
    }

    $clash->status = 'accepted';
    $clash->save();

    return $clash;
  }

  public function startClash($clash, $viewer)
  {
    if (!$clash) {
      throw new Engine_Exception('Live Clash not found.');
    }

    $streamA = Engine_Api::_()->getItem('live_stream', $clash->stream_id_a);
    $streamB = Engine_Api::_()->getItem('live_stream', $clash->stream_id_b);
    $viewerId = $viewer && $viewer->getIdentity() ? $viewer->getIdentity() : 0;

    if (!$streamA || !$streamB || !in_array($viewerId, array((int) $streamA->host_id, (int) $streamB->host_id))) {
      throw new Engine_Exception('Only a Live Clash host can start this clash.');
    }

    if ($streamA->status != Live_Api_Core::STATUS_LIVE || $streamB->status != Live_Api_Core::STATUS_LIVE) {
      throw new Engine_Exception('Both creators must still be live to start this clash.');
    }

    $table = Engine_Api::_()->getDbtable('clashes', 'live');
    $db = $table->getAdapter();
    $lockNames = $this->_acquireClashLocks($db, array((int) $streamA->getIdentity(), (int) $streamB->getIdentity()));
    try {
      $clash = $table->find((int) $clash->getIdentity())->current();
      if (!$clash || $clash->status !== 'accepted') {
        throw new Engine_Exception('This Live Clash is not ready to start.');
      }

      $config = $this->_getClashConfig();
      $now = time();
      $clash->status = 'live';
      $clash->started_at = date('Y-m-d H:i:s', $now);
      $clash->duration_seconds = $config['duration_seconds'];
      $clash->boost_multiplier = $config['boost_multiplier'];
      $clash->boost_window_seconds = $config['boost_window_seconds'];
      $clash->boost_until = null;
      $clash->next_boost_at = $this->_calculateNextBoostWindowStart($config, $now);
      $clash->winner_user_id = 0;
      $clash->mvp_user_id = 0;
      $clash->ended_at = null;
      $clash->ends_at = date('Y-m-d H:i:s', $now + $config['duration_seconds']);
      $clash->score_a = 0;
      $clash->score_b = 0;
      $clash->save();

      return $clash;
    } finally {
      $this->_releaseClashLocks($db, $lockNames);
    }
  }

  public function vote($clash, $viewer, $team)
  {
    if (!$clash) {
      throw new Engine_Exception('Live Clash not found.');
    }

    $clash = $this->_refreshClashState($clash);
    if (!$clash || $clash->status != 'live') {
      throw new Engine_Exception('Live Clash is not active.');
    }

    if (!$viewer || !$viewer->getIdentity()) {
      throw new Engine_Exception('Login required.');
    }

    $team = strtolower($team);
    if (!in_array($team, array('a', 'b'))) {
      throw new Engine_Exception('Invalid team.');
    }

    $settings = Engine_Api::_()->getApi('settings', 'core');
    Engine_Api::_()->live()->assertRateLimit('clash_vote', $clash->getIdentity(), (int) $settings->getSetting('live.rate.clash.vote.minute', 30));

    $table = Engine_Api::_()->getDbtable('clashVotes', 'live');
    $vote = $table->fetchRow($table->select()
      ->where('clash_id = ?', $clash->getIdentity())
      ->where('user_id = ?', $viewer->getIdentity())
      ->limit(1));

    if ($vote && $vote->team == $team) {
      return $vote;
    }

    if ($vote) {
      $oldStream = Engine_Api::_()->getItem('live_stream', $vote->team == 'a' ? $clash->stream_id_a : $clash->stream_id_b);
      if ($oldStream) {
        $this->addScore($clash->getIdentity(), $oldStream, -1);
      }
    } else {
      $vote = $table->createRow();
      $vote->clash_id = $clash->getIdentity();
      $vote->user_id = $viewer->getIdentity();
      $vote->creation_date = date('Y-m-d H:i:s');
    }

    $vote->team = $team;
    $vote->save();

    $this->addScore($clash->getIdentity(), Engine_Api::_()->getItem('live_stream', $team == 'a' ? $clash->stream_id_a : $clash->stream_id_b), 1);

    return $vote;
  }

  public function endClash($clash, $viewer = null)
  {
    if (!$clash) {
      throw new Engine_Exception('Live Clash not found.');
    }

    if ($viewer && $viewer->getIdentity()) {
      $streamA = Engine_Api::_()->getItem('live_stream', $clash->stream_id_a);
      $streamB = Engine_Api::_()->getItem('live_stream', $clash->stream_id_b);
      if (!$streamA || !$streamB || !in_array((int) $viewer->getIdentity(), array((int) $streamA->host_id, (int) $streamB->host_id))) {
        throw new Engine_Exception('Only a Live Clash host can end this clash.');
      }
    }

    if ($clash->status == 'ended') {
      return $clash;
    }

    $winnerStreamId = 0;
    if ((int) $clash->score_a > (int) $clash->score_b) {
      $winnerStreamId = (int) $clash->stream_id_a;
    } elseif ((int) $clash->score_b > (int) $clash->score_a) {
      $winnerStreamId = (int) $clash->stream_id_b;
    }

    if ($winnerStreamId) {
      $winnerStream = Engine_Api::_()->getItem('live_stream', $winnerStreamId);
      $clash->winner_user_id = $winnerStream ? (int) $winnerStream->host_id : 0;
    } else {
      $clash->winner_user_id = 0;
    }

    $clash->mvp_user_id = $this->_getMvpUserId($clash);
    $clash->status = 'ended';
    $clash->boost_until = null;
    $clash->next_boost_at = null;
    $clash->ended_at = date('Y-m-d H:i:s');
    $clash->save();

    return $clash;
  }

  public function addScore($clashId, $stream, $points)
  {
    $stream = $this->_normalizeStreamForClash($stream);
    if (!$stream) {
      return null;
    }

    $clash = Engine_Api::_()->getDbtable('clashes', 'live')->find((int) $clashId)->current();
    if (!$clash) {
      return null;
    }

    $clash = $this->_refreshClashState($clash);
    if (!$clash || $clash->status != 'live') {
      return null;
    }

    $streamId = (int) $stream->getIdentity();
    $scoreable = null;
    if ((int) $clash->stream_id_a == $streamId) {
      $scoreable = 'a';
    } elseif ((int) $clash->stream_id_b == $streamId) {
      $scoreable = 'b';
    }

    if ($scoreable === null) {
      return null;
    }

    $points = (int) $points;
    if ($points == 0) {
      return $clash;
    }

    $appliedPoints = $points;
    if ($points > 0 && $this->_isBoostActive($clash)) {
      $appliedPoints = max(1, (int) $points * max(1, (int) $clash->boost_multiplier));
    }

    $scoreColumn = $scoreable == 'a' ? 'score_a' : 'score_b';
    $clashTable = Engine_Api::_()->getDbtable('clashes', 'live');
    $clashTable->update(array(
      $scoreColumn => new Zend_Db_Expr('GREATEST(0, `' . $scoreColumn . '` + ' . (int) $appliedPoints . ')'),
    ), array(
      'clash_id = ?' => (int) $clash->getIdentity(),
      'status = ?' => 'live',
    ));

    $participantTable = Engine_Api::_()->getDbtable('clashParticipants', 'live');
    $participantTable->update(array(
      'score' => new Zend_Db_Expr('GREATEST(0, `score` + ' . (int) $appliedPoints . ')'),
    ), array(
      'clash_id = ?' => (int) $clash->getIdentity(),
      'stream_id = ?' => (int) $stream->getIdentity(),
    ));

    return $clashTable->find((int) $clash->getIdentity())->current();
  }

  public function serializeClash($clash)
  {
    if (!$clash) {
      return null;
    }

    $clash = $this->_refreshClashState($clash);
    if (!$clash) {
      return null;
    }

    $streamA = Engine_Api::_()->getItem('live_stream', $clash->stream_id_a);
    $streamB = Engine_Api::_()->getItem('live_stream', $clash->stream_id_b);
    $viewer = Engine_Api::_()->user()->getViewer();
    $viewerId = $viewer && $viewer->getIdentity() ? (int) $viewer->getIdentity() : 0;
    $viewerTeam = null;

    if ($streamA && $viewerId && (int) $streamA->host_id == $viewerId) {
      $viewerTeam = 'a';
    } elseif ($streamB && $viewerId && (int) $streamB->host_id == $viewerId) {
      $viewerTeam = 'b';
    }

    $now = time();
    $endsAt = $clash->ends_at ? strtotime($clash->ends_at) : 0;
    $durationSeconds = isset($clash->duration_seconds) ? (int) $clash->duration_seconds : 0;
    if (!$durationSeconds && $endsAt && $clash->started_at) {
      $durationSeconds = max(0, $endsAt - strtotime($clash->started_at));
    }

    $remainingSeconds = null;
    if ($clash->status == 'live' && $endsAt > 0) {
      $remainingSeconds = max(0, $endsAt - $now);
    }

    $boostUntil = !empty($clash->boost_until) ? strtotime($clash->boost_until) : 0;
    $boostActive = $boostUntil > $now;
    $boostStartsAt = 0;
    $boostRemaining = null;
    $boostWindow = isset($clash->boost_window_seconds) ? (int) $clash->boost_window_seconds : 0;
    if ($boostActive && $boostWindow > 0) {
      $boostStartsAt = $boostUntil - $boostWindow;
      $boostRemaining = max(0, $boostUntil - $now);
    }

    $nextBoostAt = !empty($clash->next_boost_at) ? strtotime($clash->next_boost_at) : 0;
    $boostNextIn = (!$boostActive && $nextBoostAt > $now) ? max(0, $nextBoostAt - $now) : null;

    $hasStarted = (bool) (!empty($clash->started_at) || $clash->status == 'live');
    if (!$hasStarted && !$clash->duration_seconds) {
      $durationSeconds = $durationSeconds ?: 300;
    }

    return array(
      'clash_id' => (int) $clash->clash_id,
      'status' => $clash->status,
      'stream_id_a' => (int) $clash->stream_id_a,
      'stream_id_b' => (int) $clash->stream_id_b,
      'score_a' => (int) $clash->score_a,
      'score_b' => (int) $clash->score_b,
      'winner_user_id' => (int) $clash->winner_user_id,
      'mvp_user_id' => (int) $clash->mvp_user_id,
      'started_at' => $clash->started_at,
      'ends_at' => $clash->ends_at,
      'ended_at' => $clash->ended_at,
      'duration_seconds' => $durationSeconds,
      'remaining_seconds' => $remainingSeconds,
      'viewer_team' => $viewerTeam,
      'can_accept' => $viewerTeam == 'b' && $clash->status == 'pending',
      'can_start' => in_array($viewerTeam, array('a', 'b')) && $clash->status == 'accepted',
      'can_end' => in_array($viewerTeam, array('a', 'b')) && $clash->status == 'live',
      'boost_active' => $boostActive ? 1 : 0,
      'boost_multiplier' => (int) ($clash->boost_multiplier ?: 1),
      'boost_window_seconds' => (int) ($clash->boost_window_seconds ?: 0),
      'boost_remaining' => $boostRemaining,
      'boost_starts_at' => $boostStartsAt ? date('Y-m-d H:i:s', $boostStartsAt) : null,
      'boost_ends_at' => $clash->boost_until,
      'boost_next_in' => $boostNextIn,
      'streams' => array(
        'a' => $this->_serializeClashStream($streamA),
        'b' => $this->_serializeClashStream($streamB),
      ),
    );
  }

  public function getActiveClashForStream($stream)
  {
    if (!$stream) {
      return null;
    }

    $table = Engine_Api::_()->getDbtable('clashes', 'live');
    $db = $table->getAdapter();
    $tableName = $table->info('name');
    $streamId = (int) $stream->getIdentity();
    $select = $table->select()
      ->from($tableName)
      ->where($db->quoteInto('stream_id_a = ?', $streamId) . ' OR ' . $db->quoteInto('stream_id_b = ?', $streamId))
      ->where('status IN (?)', array('pending', 'accepted', 'live'))
      ->order(new Zend_Db_Expr("FIELD(status, 'live', 'accepted', 'pending')"))
      ->order('clash_id DESC')
      ->limit(1);

    $clash = $table->fetchRow($select);
    if (!$clash) {
      return null;
    }

    if ($clash->status == 'live' && $clash->ends_at && strtotime($clash->ends_at) <= time()) {
      $clash = $this->endClash($clash);
    } else {
      $clash = $this->_refreshClashState($clash);
    }

    return $clash;
  }

  public function getClashBetweenStreams($streamA, $streamB, array $statuses = array('pending', 'accepted', 'live'))
  {
    $streamA = $streamA && is_object($streamA) ? (int) $streamA->getIdentity() : (int) $streamA;
    $streamB = $streamB && is_object($streamB) ? (int) $streamB->getIdentity() : (int) $streamB;

    if (!$streamA || !$streamB) {
      return null;
    }

    $table = Engine_Api::_()->getDbtable('clashes', 'live');
    $db = $table->getAdapter();
    $tableName = $table->info('name');
    $pairA = '(' . $db->quoteInto('stream_id_a = ?', $streamA) . ' AND ' . $db->quoteInto('stream_id_b = ?', $streamB) . ')';
    $pairB = '(' . $db->quoteInto('stream_id_a = ?', $streamB) . ' AND ' . $db->quoteInto('stream_id_b = ?', $streamA) . ')';
    $select = $table->select()
      ->from($tableName)
      ->where($pairA . ' OR ' . $pairB)
      ->order(new Zend_Db_Expr("FIELD(status, 'live', 'accepted', 'pending')"))
      ->order('clash_id DESC')
      ->limit(1);

    if (!empty($statuses)) {
      $select->where('status IN (?)', array_values(array_unique(array_map('strval', $statuses))));
    }

    return $table->fetchRow($select);
  }

  public function getCandidateStreams($stream, $limit = 12)
  {
    if (!$stream) {
      return array();
    }

    $table = Engine_Api::_()->getDbtable('streams', 'live');
    $select = $table->select()
      ->where('stream_id != ?', $stream->getIdentity())
      ->where('host_id != ?', $stream->host_id)
      ->where('status = ?', Live_Api_Core::STATUS_LIVE)
      ->where('allow_clash = ?', 1)
      ->order('viewer_count DESC')
      ->order('started_at DESC')
      ->limit(max(1, min(30, (int) $limit)));

    return $table->fetchAll($select);
  }

  protected function _serializeClashStream($stream)
  {
    if (!$stream) {
      return null;
    }

    $owner = $stream->getOwner();
    return array(
      'stream_id' => (int) $stream->getIdentity(),
      'title' => $stream->getTitle(),
      'href' => $stream->getHref(),
      'viewer_count' => (int) $stream->viewer_count,
      'host' => $owner ? array(
        'user_id' => (int) $owner->getIdentity(),
        'displayname' => Engine_Api::_()->live()->getUserDisplayName($owner),
        'photo' => $owner->getPhotoUrl('thumb.icon'),
      ) : null,
    );
  }

  protected function _createParticipant($table, $clash, $stream, $team)
  {
    $participant = $table->createRow();
    $participant->clash_id = $clash->getIdentity();
    $participant->stream_id = $stream->getIdentity();
    $participant->user_id = $stream->host_id;
    $participant->team = $team;
    $participant->score = 0;
    $participant->creation_date = date('Y-m-d H:i:s');
    $participant->save();
    return $participant;
  }

  protected function _getMvpUserId($clash)
  {
    $table = Engine_Api::_()->getDbtable('giftSends', 'live');
    $tableName = $table->info('name');
    $select = $table->select()
      ->from($tableName, array('sender_id', 'total_coins' => new Zend_Db_Expr('SUM(coins)')))
      ->where('clash_id = ?', $clash->getIdentity())
      ->group('sender_id')
      ->order('total_coins DESC')
      ->limit(1);

    $row = $table->fetchRow($select);
    return $row ? (int) $row->sender_id : 0;
  }

  protected function _normalizeStreamForClash($stream)
  {
    if (!$stream) {
      return null;
    }

    if (is_object($stream) && method_exists($stream, 'getIdentity')) {
      return $stream->getIdentity() ? $stream : null;
    }

    $stream = Engine_Api::_()->getItem('live_stream', (int) $stream);
    return $stream && $stream->getIdentity() ? $stream : null;
  }

  protected function _getClashConfig()
  {
    $settings = Engine_Api::_()->getApi('settings', 'core');

    $duration = (int) $settings->getSetting('live.clash.duration.seconds', 300);
    $boostWindow = (int) $settings->getSetting('live.clash.boost.duration.seconds', 30);
    $boostWindow = max(10, min(120, $boostWindow));

    $interval = (int) $settings->getSetting('live.clash.boost.interval.seconds', 120);
    $interval = max(60, min(1800, $interval));

    $randomMin = (int) $settings->getSetting('live.clash.boost.random.min.seconds', 60);
    $randomMax = (int) $settings->getSetting('live.clash.boost.random.max.seconds', 120);
    if ($randomMax <= 0) {
      $randomMax = $randomMin;
    }
    if ($randomMin > $randomMax) {
      $tmp = $randomMin;
      $randomMin = $randomMax;
      $randomMax = $tmp;
    }

    $multiplier = (int) $settings->getSetting('live.clash.boost.multiplier', 2);

    return array(
      'duration_seconds' => max(60, min(1800, $duration)),
      'boost_window_seconds' => $boostWindow,
      'boost_interval_seconds' => $interval,
      'boost_random_min_seconds' => max(30, min(1800, $randomMin)),
      'boost_random_max_seconds' => max(30, min(1800, $randomMax)),
      'boost_multiplier' => max(1, min(20, $multiplier)),
    );
  }

  protected function _refreshClashState($clash)
  {
    if (!$clash || (string) $clash->status !== 'live') {
      return $clash;
    }

    if ($clash->ends_at && strtotime($clash->ends_at) <= time()) {
      return $this->endClash($clash);
    }

    $changed = $this->_refreshClashBoostState($clash);
    if ($changed) {
      $clash->save();
    }

    return $clash;
  }

  protected function _refreshClashBoostState($clash)
  {
    $config = $this->_getClashConfig();
    if ((int) $config['boost_multiplier'] <= 1 || (int) $config['boost_window_seconds'] <= 0) {
      if (!empty($clash->boost_until) || !empty($clash->next_boost_at)) {
        $clash->boost_until = null;
        $clash->next_boost_at = null;
        return true;
      }
      return false;
    }

    $now = time();
    $changed = false;

    $boostUntil = $clash->boost_until ? strtotime($clash->boost_until) : 0;
    if ($boostUntil && $boostUntil <= $now) {
      $clash->boost_until = null;
      $changed = true;
      $boostUntil = 0;
    }

    $clashEnd = $clash->ends_at ? strtotime($clash->ends_at) : 0;
    $nextBoostAt = $clash->next_boost_at ? strtotime($clash->next_boost_at) : 0;

    if (!$nextBoostAt && $clashEnd > $now) {
      $clash->next_boost_at = $this->_calculateNextBoostWindowStart($config, $now);
      $changed = true;
      $nextBoostAt = strtotime($clash->next_boost_at);
    }

    if (!$boostUntil && $nextBoostAt && $nextBoostAt <= $now && (!$clashEnd || $nextBoostAt < $clashEnd)) {
      $clash->boost_until = date('Y-m-d H:i:s', $now + $config['boost_window_seconds']);
      $clash->next_boost_at = $this->_calculateNextBoostWindowStart($config, $now + $config['boost_window_seconds']);
      $changed = true;
      $boostUntil = strtotime($clash->boost_until);
    }

    if ($clashEnd > 0 && $clash->next_boost_at) {
      $next = strtotime($clash->next_boost_at);
      if ($next && $next >= $clashEnd) {
        $clash->next_boost_at = null;
        $changed = true;
      }
    }

    if (!$changed && !$clash->next_boost_at && !$clash->boost_until) {
      $next = $this->_calculateNextBoostWindowStart($config, $now);
      if (!$clashEnd || strtotime($next) < $clashEnd) {
        $clash->next_boost_at = $next;
        $changed = true;
      }
    }

    return $changed;
  }

  protected function _calculateNextBoostWindowStart($config, $now = null)
  {
    $now = (int) $now;
    if (!$now) {
      $now = time();
    }

    $min = (int) $config['boost_random_min_seconds'];
    $max = (int) $config['boost_random_max_seconds'];
    if ($max > 0 && $min > 0) {
      if ($max < $min) {
        $max = $min;
      }
      $delay = mt_rand($min, $max);
    } else {
      $delay = (int) $config['boost_interval_seconds'];
    }

    $delay = max(10, $delay);
    return date('Y-m-d H:i:s', $now + $delay);
  }

  protected function _isBoostActive($clash)
  {
    $boostUntil = $clash && !empty($clash->boost_until) ? strtotime($clash->boost_until) : 0;
    return $clash && $boostUntil > time() && (int) $clash->boost_window_seconds > 0 && (int) $clash->boost_multiplier > 1;
  }

  protected function _getClashForAnyStream(array $streamIds, array $statuses)
  {
    $streamIds = array_values(array_unique(array_filter(array_map('intval', $streamIds))));
    if (!$streamIds) {
      return null;
    }

    $table = Engine_Api::_()->getDbtable('clashes', 'live');
    $db = $table->getAdapter();
    $tableName = $table->info('name');
    $streamCondition = $db->quoteInto('stream_id_a IN (?)', $streamIds)
      . ' OR ' . $db->quoteInto('stream_id_b IN (?)', $streamIds);

    return $table->fetchRow($table->select()
      ->from($tableName)
      ->where($streamCondition)
      ->where('status IN (?)', $statuses)
      ->order('clash_id DESC')
      ->limit(1));
  }

  protected function _acquireClashLocks($db, array $streamIds)
  {
    $streamIds = array_values(array_unique(array_filter(array_map('intval', $streamIds))));
    sort($streamIds, SORT_NUMERIC);
    $locks = array();

    foreach ($streamIds as $streamId) {
      $name = 'live_clash_stream_' . $streamId;
      if ((int) $db->fetchOne('SELECT GET_LOCK(?, 5)', array($name)) !== 1) {
        $this->_releaseClashLocks($db, $locks);
        throw new Engine_Exception('Live Clash is busy. Please try again.');
      }
      $locks[] = $name;
    }

    return $locks;
  }

  protected function _releaseClashLocks($db, array $lockNames)
  {
    foreach (array_reverse($lockNames) as $name) {
      try {
        $db->fetchOne('SELECT RELEASE_LOCK(?)', array($name));
      } catch (Exception $e) {
      }
    }
  }
}
