<?php

namespace Aws\CloudTrail;

use Aws\S3\S3Client;
use Guzzle\Common\Collection;

/**
 * The `Aws\CloudTrail\LogRecordIterator` provides an easy way to iterate over log records from log files generated by
 * AWS CloudTrail. CloudTrail log files contain data about your AWS API calls and are stored in Amazon S3 at a
 * predictable path based on a bucket name, a key prefix, an account ID, a region, and date information. The files are
 * gzipped and contain structured data in JSON format. This class allows you to specify options via its factory methods,
 * including a date range, and emits each log record from any log files that match the provided options.
 *
 * @yields Collection A log record containing data about an AWS API call is yielded for each iteration on this object
 */
class LogRecordIterator implements \OuterIterator
{
    /**
     * @var LogFileReader
     */
    private $logFileReader;

    /**
     * @var \Iterator
     */
    private $logFileIterator;

    /**
     * @var array
     */
    private $records;

    /**
     * @var int
     */
    private $recordIndex;

    /**
     * @param S3Client         $s3Client
     * @param CloudTrailClient $cloudTrailClient
     * @param array            $options
     *
     * @return LogRecordIterator
     */
    public static function forTrail(S3Client $s3Client, CloudTrailClient $cloudTrailClient, array $options = array())
    {
        $logFileReader = new LogFileReader($s3Client);
        $logFileIterator = LogFileIterator::forTrail($s3Client, $cloudTrailClient, $options);

        return new self($logFileReader, $logFileIterator);
    }

    /**
     * @param S3Client $s3Client
     * @param string   $s3BucketName
     * @param array    $options
     *
     * @return LogRecordIterator
     */
    public static function forBucket(S3Client $s3Client, $s3BucketName, array $options = array())
    {
        $logFileReader = new LogFileReader($s3Client);
        $logFileIterator = new LogFileIterator($s3Client, $s3BucketName, $options);

        return new self($logFileReader, $logFileIterator);
    }

    /**
     * @param S3Client $s3Client
     * @param string   $s3BucketName
     * @param string   $s3ObjectKey
     *
     * @return LogRecordIterator
     */
    public static function forFile(S3Client $s3Client, $s3BucketName, $s3ObjectKey)
    {
        $logFileReader = new LogFileReader($s3Client);
        $logFileIterator = new \ArrayIterator(array(array(
            'Bucket' => $s3BucketName,
            'Key'   => $s3ObjectKey,
        )));

        return new self($logFileReader, $logFileIterator);
    }

    /**
     * @param LogFileReader $logFileReader
     * @param \Iterator     $logFileIterator
     */
    public function __construct(LogFileReader $logFileReader, \Iterator $logFileIterator)
    {
        $this->logFileReader = $logFileReader;
        $this->logFileIterator = $logFileIterator;
        $this->records = array();
        $this->recordIndex = 0;
    }

    /**
     * Returns the current log record as a Guzzle Collection object. This object behaves like an associative array
     * except that it returns `null` on non-existent keys instead of causing an error. See the linked resources for the
     * schema of the log record data and how to work with Guzzle Collections.
     *
     * @return Collection
     * @link http://docs.aws.amazon.com/awscloudtrail/latest/userguide/eventreference.html
     * @link http://api.guzzlephp.org/class-Guzzle.Common.Collection.html
     */
    public function current()
    {
        if ($this->valid()) {
            return new Collection($this->records[$this->recordIndex]);
        } else {
            return false;
        }
    }

    public function next()
    {
        $this->recordIndex++;

        // If all the records have been exhausted, get more records from the next log file
        while (!$this->valid()) {
            $this->logFileIterator->next();
            $success = $this->loadRecordsFromCurrentLogFile();
            if (!$success) {
                // The objects iterator is exhausted as well, so stop trying
                break;
           }
        }
    }

    public function key()
    {
        if ($logFile = $this->logFileIterator->current()) {
            return $logFile['Key'] . '.' . $this->recordIndex;
        } else {
            return null;
        }
    }

    public function valid()
    {
        return isset($this->records[$this->recordIndex]);
    }

    public function rewind()
    {
        $this->logFileIterator->rewind();
        $this->loadRecordsFromCurrentLogFile();
    }

    public function getInnerIterator()
    {
        return $this->logFileIterator;
    }

    /**
     * Examines the current file in the `logFileIterator` and attempts to read it and load log records from it using
     * the `logFileReader`. This method expects that items pulled from the iterator will take the form:
     *
     *     array(
     *         'Bucket' => '...',
     *         'Key'    => '...',
     *     )
     *
     * @return bool Returns `true` if records were loaded and `false` if no records were found
     */
    private function loadRecordsFromCurrentLogFile()
    {
        $this->recordIndex = 0;
        $this->records = array();

        $logFile = $this->logFileIterator->current();
        if ($logFile && isset($logFile['Bucket']) && isset($logFile['Key'])) {
            $this->records = $this->logFileReader->read($logFile['Bucket'], $logFile['Key']);
        }

        return (bool) $logFile;
    }
}
