<?php

namespace App\Http\Controllers\Gallery;

use App\Album;
use App\Facade\Theme;
use App\Facade\UserConfig;
use App\Helpers\DbHelper;
use App\Helpers\MiscHelper;
use app\Http\Controllers\Admin\AlbumController;
use App\Http\Controllers\Controller;
use App\Http\Middleware\VerifyCsrfToken;
use App\Photo;
use App\VisitorHit;
use Guzzle\Http\Mimetypes;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Symfony\Component\HttpFoundation\Request;

class PhotoController extends Controller
{
    public function download(Request $request, $albumUrlAlias, $photoFilename)
    {
        $album = DbHelper::getAlbumByPath($albumUrlAlias);
        if (is_null($album))
        {
            App::abort(404);
            return null;
        }

        $this->authorizeForUser($this->getUser(), 'view', $album);

        if (UserConfig::get('hotlink_protection'))
        {
            $referrer = $request->headers->get('Referer');

            if (!is_null($referrer))
            {
                $hostname = parse_url($referrer, PHP_URL_HOST);
                if (strtolower($hostname) != strtolower($request->getHttpHost()))
                {
                    App::abort(403);
                    return null;
                }
            }
            else
            {
                App::abort(403);
                return null;
            }
        }

        $photo = PhotoController::loadPhotoByAlbumAndFilename($album, $photoFilename);

        $thumbnail = $request->get('t');
        if (is_null($thumbnail))
        {
            $this->authorizeForUser($this->getUser(), 'photo.download_original', $photo);
        }

        // Record the visit to the photo
        if (UserConfig::get('enable_visitor_hits'))
        {
            DB::transaction(function () use ($album, $photo, $request, $thumbnail)
            {
                $photo->hits_download++;
                $photo->save();

                VisitorHit::fromRequest($request, $album->id, $photo->id, (is_null($thumbnail) ? 'original' : $thumbnail));
            });
        }

        $photoStream = $album->getAlbumSource()->fetchPhotoContent($photo, $thumbnail);
        $mimeType = Mimetypes::getInstance()->fromFilename($photo->storage_file_name);

        return response()->stream(
            function() use ($photoStream)
            {
                echo $photoStream;
            },
            200,
            [
                'Content-Length' => $photoStream->getContentLength(),
                'Content-Type' => $mimeType
            ]
        );
    }

    public function show(Request $request, $albumUrlAlias, $photoFilename)
    {
        $album = DbHelper::getAlbumByPath($albumUrlAlias);
        if (is_null($album))
        {
            App::abort(404);
            return null;
        }

        $this->authorizeForUser($this->getUser(), 'view', $album);

        $photo = PhotoController::loadPhotoByAlbumAndFilename($album, $photoFilename);

        $isOriginalAllowed = Gate::forUser($this->getUser())->allows('photo.download_original', $photo);

        // Load the Next/Previous buttons
        $thisPhotoDate = is_null($photo->taken_at) ? $photo->created_at : $photo->taken_at;

        // I don't like the idea of using a totally raw SQL query, but it's the only sure-fire way to number the rows
        // so we can get the previous/next photos accurately - and we don't have to load all data for the photo objects
        $previousPhoto = null;
        $nextPhoto = null;

        $allAlbumPhotos = DB::select(
            DB::raw(
                'SELECT p.id, (@row_number:=@row_number + 1) AS row_number
                FROM photos p, (SELECT @row_number:=0) AS t
                WHERE p.album_id = :album_id
              ORDER BY COALESCE(p.taken_at, p.created_at), p.name, p.id;'
            ),
            [
                'album_id' => $album->id
            ]
        );

        for ($i = 0; $i < count($allAlbumPhotos); $i++)
        {
            if ($allAlbumPhotos[$i]->id === $photo->id)
            {
                if ($i > 0)
                {
                    $previousPhoto = Photo::where('id', $allAlbumPhotos[$i - 1]->id)->first();
                }

                if ($i + 1 < count($allAlbumPhotos))
                {
                    $nextPhoto = Photo::where('id', $allAlbumPhotos[$i + 1]->id)->first();
                }

                break;
            }
        }

        // Record the visit to the photo
        if (UserConfig::get('enable_visitor_hits'))
        {
            DB::transaction(function () use ($album, $photo, $request)
            {
                $photo->hits++;
                $photo->save();

                VisitorHit::fromRequest($request, $album->id, $photo->id);
            });
        }

        return Theme::render('gallery.photo', [
            'album' => $album,
            'is_original_allowed' => $isOriginalAllowed,
            'next_photo' => $nextPhoto,
            'photo' => $photo,
            'previous_photo' => $previousPhoto
        ]);
    }

    public function showExifData(Request $request, $albumUrlAlias, $photoFilename)
    {
        $album = DbHelper::getAlbumByPath($albumUrlAlias);
        if (is_null($album))
        {
            App::abort(404);
            return null;
        }

        $this->authorizeForUser($this->getUser(), 'view', $album);

        $photo = PhotoController::loadPhotoByAlbumAndFilename($album, $photoFilename);
        $this->authorizeForUser($this->getUser(), 'changeMetadata', $photo);

        $exifData = print_r(unserialize(base64_decode($photo->raw_exif_data)), true);

        return Theme::render('gallery.photo_exif', [
            'album' => $album,
            'exif_data' => $exifData,
            'photo' => $photo
        ]);
    }

    /**
     * @param $id
     * @return Photo
     */
    public static function loadPhotoByAlbumAndFilename(Album $album, $filename)
    {
        $photo = Photo::where([
            ['album_id', $album->id],
            ['storage_file_name', $filename]
        ])->first();

        if (is_null($photo))
        {
            App::abort(404);
            return null;
        }

        return $photo;
    }
}