#include "tools.h"
#include "cfg.h"
#include "globaldefine.h"
#include "wProgramManager/pagelistitem.h"
#include "LoUIClass/x_uimsgboxok.h"
#include <QMediaPlayer>
#include <QSettings>
#include <QProcess>
#include <QBuffer>
#include <QJsonArray>
extern "C"{
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}

void Tools::timerEvent(QTimerEvent *event) {
    if(timer_id==event->timerId()) emit sTick();
}

Tools* Tools::getInstance() {
    static const auto ins = new Tools(qApp);
    return ins;
}

QRectF Tools::centerRect(qreal width, qreal height, int maxW, int maxH) {
    if(maxW < width || maxH < height) {
        auto rate = qMin(maxW / width, maxH / height);
        width *= rate;
        height *= rate;
    }
    return QRectF((maxW - width) / 2, (maxH - height) / 2, width, height);
}

QString Tools::addSufix(QString path) {
    int i = path.lastIndexOf('.');
    QString prefix = path, sufix;
    if(i > 0 && i > path.lastIndexOf('/')+1 && i >= path.length()-9) {
        prefix = path.left(i);
        sufix = path.mid(i);
    }
    i = 1;
    while(QFileInfo::exists(path = prefix+QString::number(i)+sufix)) i++;
    return path;
}
QString Tools::videoInfo(QByteArray url, QImage &img, int64_t *dur, AVCodecID *codec_id) {
    AVFormatContext *fmt_ctx = avformat_alloc_context();
    QString err;
    {
        if(avformat_open_input(&fmt_ctx, url.constData(), nullptr, nullptr) != 0) {
            err = "Couldn't open input stream";
            goto free;
        }
        if(avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
            err = "Couldn't find stream information";
            goto free;
        }
        if(dur!=nullptr) *dur = fmt_ctx->duration;
        int vi_idx = -1;
        for(uint ss=0; ss<fmt_ctx->nb_streams; ss++) if(fmt_ctx->streams[ss]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) vi_idx = ss;
        if(vi_idx == -1) {
            err = "Didn't find a Video Stream";
            goto free;
        }
        auto codecpar = fmt_ctx->streams[vi_idx]->codecpar;
        if(codec_id!=nullptr) *codec_id = codecpar->codec_id;
        qDebug()<<"codec_id"<<codecpar->codec_id<<avcodec_get_name(codecpar->codec_id);
        if(av_seek_frame(fmt_ctx, -1, 1000000, AVSEEK_FLAG_BACKWARD) < 0) {
            err = "av_seek_frame fail";
            goto free;
        }
        const AVCodec *decoder = avcodec_find_decoder(codecpar->codec_id);
        if(decoder==nullptr) {
            err = "Could not found Video Decoder";
            goto free;
        }
        auto vcCtx = avcodec_alloc_context3(decoder);
        avcodec_parameters_to_context(vcCtx, codecpar);
        if(avcodec_open2(vcCtx, decoder, nullptr) < 0) {
            err = "Could not open Video Codec Ctx";
            avcodec_free_context(&vcCtx);
            goto free;
        }
        auto sws_ctx = sws_getContext(vcCtx->width, vcCtx->height, vcCtx->pix_fmt, vcCtx->width, vcCtx->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
        auto packet = av_packet_alloc();
        auto frm = av_frame_alloc();
        while(1) {
            if(av_read_frame(fmt_ctx, packet) < 0) break;
            if(packet->stream_index != vi_idx) continue;
            int res = avcodec_send_packet(vcCtx, packet);
            if(res < 0) break;
            while((res = avcodec_receive_frame(vcCtx, frm)) != AVERROR(EAGAIN)) {
                if(res < 0) goto free2;
                img = QImage(vcCtx->width, vcCtx->height, QImage::Format_RGB888);
                uint8_t *dst[4]{img.bits()};
                int dstStride[4]{img.bytesPerLine()};
                sws_scale(sws_ctx, frm->data, frm->linesize, 0, vcCtx->height, dst, dstStride);
                goto free2;
            }
        }
        free2:
        av_frame_free(&frm);
        av_packet_free(&packet);
        avcodec_free_context(&vcCtx);
        sws_freeContext(sws_ctx);
    }
    free:
    avformat_close_input(&fmt_ctx);
    return err;
}
QString Tools::audioInfo(QByteArray url, int64_t *dur) {
    AVFormatContext *fmt_ctx = avformat_alloc_context();
    QString err;
    {
        if(avformat_open_input(&fmt_ctx, url.constData(), nullptr, nullptr) != 0) {
            err = "Couldn't open input stream";
            goto free;
        }
        if(avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
            err = "Couldn't find stream information";
            goto free;
        }
        if(dur!=nullptr) *dur = fmt_ctx->duration;
    }
    free:
    avformat_close_input(&fmt_ctx);
    return err;
}
QString Tools::readErrStr(QImageReader::ImageReaderError err) {
    if(err==QImageReader::UnknownError) return "UnknownError";
    if(err==QImageReader::FileNotFoundError) return "FileNotFoundError";
    if(err==QImageReader::DeviceError) return "DeviceError";
    if(err==QImageReader::UnsupportedFormatError) return "UnsupportedFormatError";
    if(err==QImageReader::InvalidDataError) return "InvalidDataError";
    return QString::number(err);
}

QString Tools::fileMd5(QString filePath) {
    QFile file(filePath);
    if(! file.open(QFile::ReadOnly)) return QString();
    QCryptographicHash cryptoHash(QCryptographicHash::Md5);
    if(! cryptoHash.addData(&file)) {
        file.close();
        return QString();
    }
    file.close();
    return QString::fromLatin1(cryptoHash.result().toHex());
}
void Tools::mergeFormat(QTextEdit *textEdit, const QTextCharFormat &format) {
    QTextCursor cursor = textEdit->textCursor();
    if(! cursor.hasSelection()) cursor.select(QTextCursor::WordUnderCursor);
    cursor.mergeCharFormat(format);
    textEdit->mergeCurrentCharFormat(format);
}
void Tools::saveImg(const QString& dir, const QFontMetrics& metric, const QFont& font, const QColor& color, QJsonObject& imgs, const QString& str, const QString& name) {
    if(str.isEmpty()) return;
    QImage img(metric.horizontalAdvance(str), metric.lineSpacing(), QImage::Format_ARGB32);
    img.fill(Qt::transparent);
    QPainter painter(&img);
    painter.setFont(font);
    painter.setPen(color);
    QTextOption opt(Qt::AlignCenter);
    opt.setWrapMode(QTextOption::NoWrap);
    painter.drawText(QRectF(0, 0, img.width(), img.height()), str, opt);
    QBuffer buf;
    if(! img.save(&buf, "PNG")) return;
    QCryptographicHash cryptoHash(QCryptographicHash::Md5);
    cryptoHash.addData(buf.data());
    auto md5 = QString::fromLatin1(cryptoHash.result().toHex());
    QFile file(dir+"/"+md5);
    if(! file.open(QFile::WriteOnly)) return;
    file.write(buf.data());
    file.close();
    imgs.insert(name, md5);
}
void Tools::saveImg2(const QString& dir, const QFontMetrics& metric, const QFont& font, const QColor& color, QJsonArray& imgs, const QString& str, const QString& name) {
    QJsonObject obj{
        {"name", name},
        {"mime", "image/png"}
    };
    if(! str.isEmpty()) {
        QImage img(metric.horizontalAdvance(str), metric.lineSpacing(), QImage::Format_ARGB32);
        img.fill(Qt::transparent);
        QPainter painter(&img);
        painter.setFont(font);
        painter.setPen(color);
        QTextOption opt(Qt::AlignCenter);
        opt.setWrapMode(QTextOption::NoWrap);
        painter.drawText(QRectF(0, 0, img.width(), img.height()), str, opt);
        QBuffer buf;
        if(img.save(&buf, "PNG")) {
            obj.insert("picWidth", img.width());
            obj.insert("picHeight", img.height());
            QCryptographicHash cryptoHash(QCryptographicHash::Md5);
            cryptoHash.addData(buf.data());
            auto md5 = QString::fromLatin1(cryptoHash.result().toHex());
            QFile file(dir+"/"+md5);
            if(file.open(QFile::WriteOnly)) {
                file.write(buf.data());
                file.close();
                obj.insert("id", md5);
            }
        }
    }
    imgs.append(obj);
}

QBrush Tools::getBrush(const QColor& color) {
    return color.alpha()==0 ? Qt::NoBrush : QBrush(color);
}

int Tools::color2Int(const QColor& color) {
    int res = 0;
    res |= (color.red()   & 0xFF) << 24;
    res |= (color.green() & 0xFF) << 16;
    res |= (color.blue()  & 0xFF) <<  8;
    res |= (color.alpha() & 0xFF);
    return res;
}

QColor Tools::int2Color(int value) {
    QColor res;
    res.setRed  ((value >> 24) & 0xFF);
    res.setGreen((value >> 16) & 0xFF);
    res.setBlue ((value >>  8) & 0xFF);
    res.setAlpha((value      ) & 0xFF);
    return res;
}

QString Tools::selectStr(bool f, const QString &s0, const QString &s1) {
    return f ? s0 : s1;
}

QString Tools::convertFileSize(const qint64 & bytes){
    float num = bytes;
    QStringList list;
    list << "KB" << "MB" << "GB" << "TB";

    QStringListIterator i(list);
    QString unit("bytes");

    while(num >= 1024.0 && i.hasNext())
    {
     unit = i.next();
     num /= 1024.0;
    }
    return QString().setNum(num,'f',2)+" "+unit;
}
QString Tools::styleSheet() {
    QStringList qsses = QDir(":/qss").entryList(QStringList{"*.css"});
    std::sort(qsses.begin(), qsses.end(), [](const QString &a, const QString &b) {
        return a < b;
    });
    QString qss;
    foreach(QString qss_name, qsses) {
        QFile f(":/qss/" + qss_name);
        if(f.exists() && f.open(QFile::ReadOnly)) {
            qss += f.readAll();
            f.close();
        }
    }
    return qss;
}