package com.yutou.nas.utils;

import com.yutou.nas.Datas.AppData;
import com.yutou.nas.mybatis.dao.MusicDataDao;
import com.yutou.nas.mybatis.model.MusicData;
import com.yutou.nas.mybatis.model.MusicDataExample;
import com.yutou.nas.utils.Interfaces.IMusicToolsService;

import ealvatag.audio.AudioFile;
import ealvatag.audio.AudioFileIO;
import ealvatag.audio.AudioHeader;
import ealvatag.tag.FieldKey;
import ealvatag.tag.NullTag;
import ealvatag.tag.Tag;
import ealvatag.tag.images.NullArtwork;
import net.bramp.ffmpeg.FFprobe;
import net.bramp.ffmpeg.probe.FFmpegFormat;
import net.bramp.ffmpeg.probe.FFmpegProbeResult;
import net.bramp.ffmpeg.probe.FFmpegStream;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Service("MusicToolsService")
public class MusicToolsServiceImpl implements IMusicToolsService {
    public static final int FIND_TITLE = 1;
    public static final int FIND_ARTIST = 2;

    private String musicPath = "/media/yutou/4t/public/音乐";
    private boolean isScan = false;

    @Resource
    MusicDataDao musicDataDao;

    @Override
    public void init() {
        musicPath = (String) ConfigTools.load(ConfigTools.CONFIG, "musicDir");
        scanMusic();
    }

    @Override
    public void scanMusic() {
        if (isScan) {
            return;
        }
        if (ConfigTools.load(ConfigTools.CONFIG, "musicScan").equals("false")) {
            return;
        }
        musicPath = (String) ConfigTools.load(ConfigTools.CONFIG, "musicDir");
        musicDataDao.truncate();
        com.yutou.nas.utils.Log.i("执行扫描:" + musicPath);
        new Thread(() -> {
            long startTime = System.currentTimeMillis();
            QQBotManager.getInstance().sendMessage("开始扫描音乐夹");
            isScan = true;
            scan(new File(musicPath));
            isScan = false;
            com.yutou.nas.utils.Log.i("扫描完成");
            QQBotManager.getInstance().sendMessage("音乐扫描完成,共" + getLength() + "首歌,耗时:"
                    + (System.currentTimeMillis() - startTime));
        }).start();
    }


    private void scan(File path) {
        if (path.isFile()) {
            add(path);
        } else if (path.isDirectory()) {
            for (File file : Objects.requireNonNull(path.listFiles())) {
                if (file.isDirectory()) {
                    scan(file);
                } else {
                    add(file);
                }
            }
        }
    }

    public void getPathOrDir(String path, List<MusicData> list) {
        File files = new File(path);
        for (File file : files.listFiles()) {
            if (file.isFile()) {
                list.add(getMetadata(file));
            } else {
                getPathOrDir(file.getAbsolutePath(), list);
            }
        }
    }


    /**
     * 获取指定目录下的音乐
     *
     * @param path  指定目录
     * @param isDir 是否扫描目录下的所有文件，false则仅为当前目录
     * @return 音乐列表
     */
    @Override
    public List<MusicData> getPath(String path, boolean isDir) {
        List<MusicData> list = new ArrayList<>();
        List<MusicData> main = new ArrayList<>();
        MusicDataExample example = new MusicDataExample();
        String replacement = ConfigTools.load(ConfigTools.CONFIG, "os").equals("windows") ? "\\\\" : "/";
        String tmpPath = path;
        if (isDir) {
            example.createCriteria().andFileLike(tmpPath.replace(File.separator, replacement) + "%");
            main = musicDataDao.selectByExample(example);
        }
        tmpPath = tmpPath.replace(File.separator, replacement)
                .replace("[", "\\[")
                .replace("(", "\\(")
                .replace(")", "\\)")
                .replace("]", "\\]");
        main.addAll(musicDataDao.selectByRegexp(tmpPath + replacement + "([^" + replacement + "]+)$"));

        if (!path.equals(AppData.defaultMusicPath) && !path.equals("root")) {
            MusicData t2 = new MusicData();
            t2.setTitle("返回");
            if (main.isEmpty()) {
                t2.setFile("root");
            } else {
                MusicData tmp = main.get(0);
                t2.setFile(new File(tmp.getLastdir()).getAbsolutePath());
            }
            com.yutou.nas.utils.Log.i("查询地址:" + path + " 设置返回地址:" + t2.getFile());
            t2.setIsdir(1);
            list.add(t2);
        }
        getDirList(path, list);
        list.addAll(main);
        return list;
    }

    public List<String> getAllAlbum() {
        return musicDataDao.selectAllAlbum();
    }

    public List<String> getAllArtist() {
        return musicDataDao.selectAllArtist();
    }

    public List<MusicData> selectAlbum(String album) {
        return musicDataDao.selectAlbum(album);
    }

    public List<MusicData> selectArtist(String artist) {
        return musicDataDao.selectArtist(artist);
    }

    private void getDirList(String path, List<MusicData> list) {
        File file = new File(path);
        com.yutou.nas.utils.Log.i("扫描文件:"+path);
        if(file.isDirectory()) {
            for (File listFile : file.listFiles()) {
                if (listFile.isDirectory()) {
                    MusicData data = new MusicData();
                    data.setTitle(listFile.getName());
                    data.setIsdir(1);
                    data.setFile(listFile.getAbsolutePath());
                    list.add(data);
                }
            }
        }else{
            MusicData data = new MusicData();
            data.setTitle(file.getName());
            data.setIsdir(0);
            data.setFile(file.getAbsolutePath());
            list.add(data);
        }
    }

    private void add(File file) {
        MusicData data = getMetadata(file);
        if (data != null) {
            try {
                musicDataDao.insert(data);
            } catch (Exception e) {
                e.printStackTrace();
                QQBotManager.getInstance().sendMessage("音乐文件添加失败:" + data.toString());
            }

        }
    }

    @Override
    public MusicData getMusicData(String path) {
        MusicDataExample example = new MusicDataExample();
        example.createCriteria().andFileEqualTo(path);
        List<MusicData> list = musicDataDao.selectByExample(example);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }


    public MusicData getMetadata(File file) {
        try {
            if (file.getName().endsWith(".lrc")
                    || file.getName().endsWith(".jpg")
                    || file.getName().endsWith(".ini")
                    || file.getName().endsWith(".png")
                    || file.getName().endsWith(".torrent")
                    || file.getName().endsWith(".log")
                    || file.getName().endsWith(".mkv")
                    || file.getName().endsWith(".dff")
                    || file.getName().endsWith(".cue")
                    || file.getName().endsWith(".m3u")
            ) {
                return null;
            }

            AudioFile audioFile = AudioFileIO.read(file);
            Tag tag = audioFile.getTag().or(NullTag.INSTANCE);
            MusicData data = new MusicData();
            try {
                data.setAlbum(tag.getFirst(FieldKey.ALBUM));
            } catch (Exception e) {
            }
            try {
                data.setArtist(tag.getFirst(FieldKey.ARTIST));
            } catch (Exception e) {
            }
            try {
                data.setArtistSort(tag.getFirst(FieldKey.ARTIST_SORT));
            } catch (Exception e) {
            }
            try {
                data.setComment(tag.getFirst(FieldKey.COMMENT));
            } catch (Exception e) {
            }
            try {
                data.setComposer(tag.getFirst(FieldKey.COMPOSER));
            } catch (Exception e) {
            }
            try {
                data.setDiscNo(tag.getFirst(FieldKey.DISC_NO));
            } catch (Exception e) {
            }
            try {
                if (StringUtils.isEmpty(tag.getFirst(FieldKey.TITLE))) {
                    data.setTitle(file.getName());
                } else {
                    data.setTitle(tag.getFirst(FieldKey.TITLE));
                }
            } catch (Exception e) {
                data.setTitle(file.getName());
            }
            try {
                data.setTrack(tag.getFirst(FieldKey.TRACK));
            } catch (Exception e) {
            }
            try {
                data.setYear(tag.getFirst(FieldKey.YEAR));
            } catch (Exception e) {
            }
            data.setFile(file.getAbsolutePath());
            data.setIsdir(file.isDirectory() ? 1 : 0);
            data.setLastdir(file.getParentFile().getParent());

            AudioHeader header = audioFile.getAudioHeader();
            data.setBitrate(header.getBitRate());
            data.setSamplerate(header.getSampleRate());
            data.setNoofsamples(header.getNoOfSamples());
            data.setChannelcount(header.getChannelCount());
            data.setEncodingtype(header.getEncodingType());
            data.setDurationasdouble(header.getDurationAsDouble());
            data.setLossless(header.isLossless() ? 1 : 0);
            data.setVariablebitrate(header.isVariableBitRate() ? 1 : 0);
            try {
                data.setMd5(header.getClass().getMethod("getMd5").invoke(header).toString());
            } catch (Exception ignored) {
                data.setMd5(Tools.getFileMD5(file));
            }
            return data;
        } catch (IOException e) {
            return getMetadata_jthink(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public MusicData getMetadata_jthink(File file) {
        try {
            org.jaudiotagger.audio.AudioFile audioFile = org.jaudiotagger.audio.AudioFileIO.read(file);
            org.jaudiotagger.tag.Tag tag = audioFile.getTag();
            MusicData data = new MusicData();
            try {
                data.setAlbum(tag.getFirst(org.jaudiotagger.tag.FieldKey.ALBUM));
            } catch (Exception ignored) {
            }
            try {
                data.setArtist(tag.getFirst(org.jaudiotagger.tag.FieldKey.ARTIST));
            } catch (Exception ignored) {
            }
            try {
                data.setArtistSort(tag.getFirst(org.jaudiotagger.tag.FieldKey.ARTIST_SORT));
            } catch (Exception ignored) {
            }
            try {
                data.setComment(tag.getFirst(org.jaudiotagger.tag.FieldKey.COMMENT));
            } catch (Exception ignored) {
            }
            try {
                data.setComposer(tag.getFirst(org.jaudiotagger.tag.FieldKey.COMPOSER));
            } catch (Exception ignored) {
            }
            try {
                data.setDiscNo(tag.getFirst(org.jaudiotagger.tag.FieldKey.DISC_NO));
            } catch (Exception ignored) {
            }
            try {
                if (StringUtils.isEmpty(tag.getFirst(org.jaudiotagger.tag.FieldKey.TITLE))) {
                    data.setTitle(file.getName());
                } else {
                    data.setTitle(tag.getFirst(org.jaudiotagger.tag.FieldKey.TITLE));
                }
            } catch (Exception e) {
                data.setTitle(file.getName());
            }
            try {
                data.setTrack(tag.getFirst(org.jaudiotagger.tag.FieldKey.TRACK));
            } catch (Exception ignored) {
            }
            try {
                data.setYear(tag.getFirst(org.jaudiotagger.tag.FieldKey.YEAR));
            } catch (Exception ignored) {
            }
            data.setFile(file.getAbsolutePath());
            data.setIsdir(file.isDirectory() ? 1 : 0);
            data.setLastdir(file.getParentFile().getParent());

            org.jaudiotagger.audio.AudioHeader header = audioFile.getAudioHeader();
            data.setBitrate(Integer.parseInt(header.getBitRate()));
            data.setSamplerate(Integer.parseInt(header.getSampleRate()));
            data.setNoofsamples(Long.parseLong(header.getSampleRateAsNumber() + ""));
            data.setChannelcount(Integer.parseInt(header.getChannels()));
            data.setEncodingtype(header.getEncodingType() + "");
            data.setDurationasdouble(Double.parseDouble(header.getTrackLength() + ""));
            data.setLossless(header.isLossless() ? 1 : 0);
            data.setVariablebitrate(header.isVariableBitRate() ? 1 : 0);
            try {
                data.setMd5(header.getClass().getMethod("getMd5").invoke(header).toString());
            } catch (Exception ignored) {
                data.setMd5(Tools.getFileMD5(file));
            }
            return data;
        } catch (Exception e) {
            return getMetadataOfFFmpeg(file);
        }
    }

    private MusicData getMetadataOfFFmpeg(File file) {
        MusicData data;
        try {
            data = new MusicData();
            FFprobe fFprobe = new FFprobe((String) ConfigTools.load(ConfigTools.CONFIG, "ffprobe"));
            FFmpegProbeResult result = fFprobe.probe(file.getAbsolutePath());
            FFmpegFormat format = result.getFormat();
            FFmpegStream stream = null;
            for (FFmpegStream tmp : result.getStreams()) {
                if (tmp.index == 0) {
                    stream = tmp;
                }
            }
            Map<String, String> tag = format.tags;
            data.setTitle(getTitle(tag));
            data.setAlbum(getAlbum(tag));
            data.setArtist(getArtist(tag));
            data.setDiscNo(tag.get("disc") == null ? tag.get("disc".toUpperCase()) : tag.get("disc"));
            data.setTrack(tag.get("track") == null ? tag.get("track".toUpperCase()) : tag.get("track"));
            data.setYear(getYear(tag));
            data.setArtistSort(tag.get("album_artist") == null ? tag.get("album_artist".toUpperCase()) : tag.get("album_artist"));
            data.setDurationasdouble(format.duration);
            data.setBitrate((int) (format.bit_rate / 1000));
            if (stream != null) {
                if (data.getBitrate() == 0)
                    data.setBitrate((int) (stream.bit_rate / 1000));
                data.setChannelcount(stream.channels);
                data.setLossless(0);
                data.setSamplerate(stream.sample_rate);
                data.setNoofsamples(stream.duration_ts);
            }
            data.setEncodingtype(format.format_long_name);
            data.setComment("");
            data.setComposer("");
            data.setVariablebitrate(0);
            data.setFile(file.getAbsolutePath());
            data.setLastdir(file.getParentFile().getParent());
            data.setIsdir(file.isDirectory() ? 1 : 0);
            if (data.getYear() == null) {
                data.setYear("0000");
            }
            data.setMd5(Tools.getFileMD5(file));
        } catch (Exception e) {
            e.printStackTrace();
            data = new MusicData();
            data.setTitle(file.getName());
            data.setFile(file.getAbsolutePath());
            data.setIsdir(file.isDirectory() ? 1 : 0);
            data.setLastdir(file.getParentFile().getParent());
            data.setMd5(Tools.getFileMD5(file));
            QQBotManager.getInstance().sendMessage("添加音乐文件失败:\n" + data.toString() + "\n" + Tools.getExceptionString(e));
        }
        return data;
    }

    private String getTitle(Map<String, String> tag) {
        String title = tag.get("title");
        if (StringUtils.isEmpty(title)) {
            title = tag.get("TITLE");
            if (StringUtils.isEmpty(title)) {
                title = tag.get("Title");
            }
        }
        return title;
    }

    private String getArtist(Map<String, String> tag) {
        String title = tag.get("artist");
        if (StringUtils.isEmpty(title)) {
            title = tag.get("ARTIST");
            if (StringUtils.isEmpty(title)) {
                title = tag.get("Artist");
            }
        }
        return title;
    }

    private String getAlbum(Map<String, String> tag) {
        String title = tag.get("album");
        if (StringUtils.isEmpty(title)) {
            title = tag.get("ALBUM");
            if (StringUtils.isEmpty(title)) {
                title = tag.get("Album");
            }
        }
        return title;
    }

    private String getYear(Map<String, String> tag) {
        String title = tag.get("year");
        if (StringUtils.isEmpty(title)) {
            title = tag.get("YEAR");
            if (StringUtils.isEmpty(title)) {
                title = tag.get("Year");
            }
        }
        return title;
    }

    @Override
    public List<MusicData> findOfTitle(String title) {
        return find(title, FIND_TITLE);
    }

    @Override
    public List<MusicData> findOfArtist(String by) {
        return find(by, FIND_ARTIST);
    }

    public List<MusicData> getMusicList() {
        return musicDataDao.selectByExample(new MusicDataExample());
    }

    @Override
    public int getLength() {
        return musicDataDao.selectByExample(new MusicDataExample()).size();
    }

    @Override
    public boolean isScan() {
        return isScan;
    }

    private List<MusicData> find(String title, int type) {
        List<MusicData> list;
        MusicDataExample example = new MusicDataExample();
        if (type == FIND_TITLE) {
            example.createCriteria().andTitleEqualTo(title);
        } else if (type == FIND_ARTIST) {
            example.createCriteria().andArtistEqualTo(title);
        }
        list = musicDataDao.selectByExample(example);

        return list;
    }

    @Override
    public String getMusicPath() {
        return musicPath;
    }

    public void setMusicPath(String musicPath) {
        this.musicPath = musicPath;
    }

    @Override
    public byte[] readImage(String path) throws Exception {
        File file = new File(path);
        AudioFile audioFile = null;
        audioFile = AudioFileIO.read(file);
        Tag tag = audioFile.getTag().or(NullTag.INSTANCE);
        byte[] bytes = tag.getFirstArtwork().or(NullArtwork.INSTANCE).getBinaryData();
        if (bytes.length == 0) {
            return readImageFile(file);
        }
        return bytes;
    }

    private byte[] readImageFile(File file) throws Exception {
        String path = file.getAbsolutePath().replace(file.getName(), "");
        File img = new File(path, "cover.jpg");
        if (!img.exists()) {
            img = new File(path, "Cover.jpg");
            if (!img.exists()) {
                img = new File(path, "COVER.jpg");
                if (!img.exists()) {
                    throw new NullPointerException("没有cover文件");
                }
            }
        }

        return Files.readAllBytes(Paths.get(img.getAbsolutePath()));
    }

    public static void main(String[] args) throws Exception {
        File file = new File("Z:\\音乐\\总之就是非常酸\\ED\\カノエラナ - 月と星空\\カノエラナ.wav");
        file = new File("Z:\\音乐\\终将成为你\\[OP]君にふれて\\rise.flac");
        // file = new File("Z:\\音乐\\周董\\2012 十二新作\\03 公公偏头痛.ape");
        com.yutou.nas.utils.Log.i(new MusicToolsServiceImpl().getMetadataOfFFmpeg(file));
    }


}
