/*
 * Copyright (c) 2015 Meltytech, LLC
 * Author: Brian Matherly <code@brianmatherly.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "sharedframe.h"

class FrameData : public QSharedData
{
public:
    FrameData()
        : f((mlt_frame) nullptr)
    {
    }
    explicit FrameData(Mlt::Frame &frame)
        : f(frame)
    {
    }
    ~FrameData() = default;

    Mlt::Frame f;

private:
    Q_DISABLE_COPY(FrameData)
};

SharedFrame::SharedFrame()
    : d(nullptr)
{
}

SharedFrame::SharedFrame(Mlt::Frame &frame)
    : d(new FrameData(frame))
{
}

SharedFrame::SharedFrame(const SharedFrame &other)

    = default;

SharedFrame::~SharedFrame() = default;

SharedFrame &SharedFrame::operator=(const SharedFrame &other) = default;

bool SharedFrame::is_valid() const
{
    return d && d->f.is_valid();
}

Mlt::Frame SharedFrame::clone(bool audio, bool image, bool alpha) const
{
    // TODO: Consider moving this implementation into MLT.
    // It could be added to mlt_frame as an alternative to:
    //     mlt_frame mlt_frame_clone( mlt_frame self, int is_deep );
    // It could also be added to Mlt::Frame as a const function.
    void *data = nullptr;
    void *copy = nullptr;
    int size = 0;
    Mlt::Frame cloneFrame(mlt_frame_init(nullptr));
    cloneFrame.inherit(d->f);
    cloneFrame.set("_producer", d->f.get_data("_producer", size), 0, nullptr, nullptr);
    cloneFrame.set("movit.convert", d->f.get_data("movit.convert", size), 0, nullptr, nullptr);
    cloneFrame.get_frame()->convert_image = d->f.get_frame()->convert_image;
    cloneFrame.get_frame()->convert_audio = d->f.get_frame()->convert_audio;

    data = d->f.get_data("audio", size);
    if (audio && (data != nullptr)) {
        if (size == 0) {
            size = mlt_audio_format_size(get_audio_format(), get_audio_samples(), get_audio_channels());
        }
        copy = mlt_pool_alloc(size);
        memcpy(copy, data, (unsigned)size);
        cloneFrame.set("audio", copy, size, mlt_pool_release);
    } else {
        cloneFrame.set("audio", 0);
        cloneFrame.set("audio_format", mlt_audio_none);
        cloneFrame.set("audio_channels", 0);
        cloneFrame.set("audio_frequency", 0);
        cloneFrame.set("audio_samples", 0);
    }

    data = d->f.get_data("image", size);
    if (image && (data != nullptr)) {
        if (size == 0) {
            size = mlt_image_format_size(get_image_format(), get_image_width(), get_image_height(), nullptr);
        }
        copy = mlt_pool_alloc(size);
        memcpy(copy, data, (unsigned)size);
        cloneFrame.set("image", copy, size, mlt_pool_release);
    } else {
        cloneFrame.set("image", 0);
        cloneFrame.set("image_format", mlt_image_none);
        cloneFrame.set("width", 0);
        cloneFrame.set("height", 0);
    }

    data = d->f.get_data("alpha", size);
    if (alpha && (data != nullptr)) {
        if (size == 0) {
            size = get_image_width() * get_image_height();
        }
        copy = mlt_pool_alloc(size);
        memcpy(copy, data, (unsigned)size);
        cloneFrame.set("alpha", copy, size, mlt_pool_release);
    } else {
        cloneFrame.set("alpha", 0);
    }

    // Release the reference on the initial frame so that the returned frame
    // only has one reference.
    mlt_frame_close(cloneFrame.get_frame());
    return cloneFrame;
}

int SharedFrame::get_int(const char *name) const
{
    return d->f.get_int(name);
}

int64_t SharedFrame::get_int64(const char *name) const
{
    return d->f.get_int64(name);
}

double SharedFrame::get_double(const char *name) const
{
    return d->f.get_double(name);
}

char *SharedFrame::get(const char *name) const
{
    return d->f.get(name);
}

int SharedFrame::get_position() const
{
    return d->f.get_position();
}

mlt_image_format SharedFrame::get_image_format() const
{
    return (mlt_image_format)d->f.get_int("format");
}

int SharedFrame::get_image_width() const
{
    return d->f.get_int("width");
}

int SharedFrame::get_image_height() const
{
    return d->f.get_int("height");
}

const uint8_t *SharedFrame::get_image() const
{
    mlt_image_format format = get_image_format();
    int width = get_image_width();
    int height = get_image_height();
    return (uint8_t *)d->f.get_image(format, width, height, 0);
}

mlt_audio_format SharedFrame::get_audio_format() const
{
    return (mlt_audio_format)d->f.get_int("audio_format");
}

int SharedFrame::get_audio_channels() const
{
    return d->f.get_int("audio_channels");
}

int SharedFrame::get_audio_frequency() const
{
    return d->f.get_int("audio_frequency");
}

int SharedFrame::get_audio_samples() const
{
    return d->f.get_int("audio_samples");
}

const int16_t *SharedFrame::get_audio() const
{
    mlt_audio_format format = get_audio_format();
    int frequency = get_audio_frequency();
    int channels = get_audio_channels();
    int samples = get_audio_samples();
    return (int16_t *)d->f.get_audio(format, frequency, channels, samples);
}
