#   Copyright (C) 2012, Almar Klein
#   All rights reserved.
#
#   This code is subject to the (new) BSD license:
#
#   Redistribution and use in source and binary forms, with or without
#   modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the <organization> nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#
# changes of this file GRASS (PNG instead of JPG) by Anna Petrasova 2013

"""Module images2avi

Uses ffmpeg to read and write AVI files. Requires PIL

I found these sites useful:
http://www.catswhocode.com/blog/19-ffmpeg-commands-for-all-needs
http://linux.die.net/man/1/ffmpeg

"""

import os
import time
import subprocess
import shutil
from pathlib import Path

from grass.imaging import images2ims
import grass.script as gs


def _cleanDir(tempDir):
    for i in range(3):
        try:
            shutil.rmtree(tempDir)
        except Exception:
            time.sleep(0.2)  # Give OS time to free sources
        else:
            break
    else:
        print("Oops, could not fully clean up temporary files.")


def writeAvi(
    filename,
    images,
    duration=0.1,
    encoding="mpeg4",
    inputOptions="",
    outputOptions="",
    bg_task=False,
):
    """Export movie to a AVI file, which is encoded with the given
    encoding. Hint for Windows users: the 'msmpeg4v2' codec is
    natively supported on Windows.

    Images should be a list consisting of PIL images or numpy arrays.
    The latter should be between 0 and 255 for integer types, and
    between 0 and 1 for float types.

    Requires the "ffmpeg" application:

    * Most linux users can install using their package manager
    * There is a windows installer on the visvis website

    :param str filename: output filename
    :param images:
    :param float duration:
    :param str encoding: the encoding type
    :param inputOptions:
    :param outputOptions:
    :param bool bg_task: if thread background task, not raise but
    return error message

    :return str: error message
    """

    # Get fps
    try:
        fps = float(1.0 / duration)
    except Exception:
        raise ValueError(_("Invalid duration parameter for writeAvi."))

    # Determine temp dir and create images
    tempDir = os.path.join(os.path.expanduser("~"), ".tempIms")
    images2ims.writeIms(os.path.join(tempDir, "im*.png"), images)

    # Determine formatter
    N = len(images)
    formatter = "%04d"
    if N < 10:
        formatter = "%d"
    elif N < 100:
        formatter = "%02d"
    elif N < 1000:
        formatter = "%03d"

    # Compile command to create avi
    command = "ffmpeg -r %i %s " % (int(fps), inputOptions)
    command += "-i im%s.png " % (formatter,)
    command += "-g 1 -vcodec %s %s " % (encoding, outputOptions)
    command += "output.avi"

    # Run ffmpeg
    S = subprocess.Popen(
        command, shell=True, cwd=tempDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )

    # Show what ffmpeg has to say
    outPut = S.stdout.read()

    if S.wait():
        # Clean up
        _cleanDir(tempDir)
        if bg_task:
            return (
                gs.decode(outPut)
                + "\n"
                + gs.decode(S.stderr.read())
                + "\n"
                + _("Could not write avi.")
            )
        # An error occurred, show
        print(gs.decode(outPut))
        print(gs.decode(S.stderr.read()))
        raise RuntimeError(_("Could not write avi."))
    try:
        # Copy avi
        shutil.copy(os.path.join(tempDir, "output.avi"), filename)
    except Exception as err:
        # Clean up
        _cleanDir(tempDir)
        if bg_task:
            return str(err)
        raise

    # Clean up
    _cleanDir(tempDir)


def readAvi(filename, asNumpy=True):
    """Read images from an AVI (or MPG) movie.

    Requires the "ffmpeg" application:
      * Most linux users can install using their package manager
      * There is a windows installer on the visvis website

    :param str filename: name of input movie file
    :param bool asNumpy:
    """

    # Check whether it exists
    if not Path(filename).is_file():
        raise OSError("File not found: " + str(filename))

    # Determine temp dir, make sure it exists
    tempDir = os.path.join(os.path.expanduser("~"), ".tempIms")
    Path(tempDir).mkdir(parents=True, exist_ok=True)

    # Copy movie there
    shutil.copy(filename, os.path.join(tempDir, "input.avi"))

    # Run ffmpeg
    command = "ffmpeg -i input.avi im%d.jpg"
    with subprocess.Popen(
        command,
        cwd=tempDir,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    ) as S:
        # Show what mencodec has to say
        outPut = S.stdout.read()
        if S.wait():
            # An error occurred, show
            print(outPut)
            print(S.stderr.read())
            # Clean up
            _cleanDir(tempDir)
            msg = "Could not read avi."
            raise RuntimeError(msg)
    # Read images
    images = images2ims.readIms(os.path.join(tempDir, "im*.jpg"), asNumpy)
    # Clean up
    _cleanDir(tempDir)

    # Done
    return images
