package allen.town.podcast.parser.media.id3;

import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import allen.town.podcast.model.feed.Chapter;
import allen.town.podcast.model.feed.EmbeddedChapterImage;
import allen.town.podcast.parser.media.id3.model.FrameHeader;
import org.apache.commons.io.input.CountingInputStream;

import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

/**
 * Reads ID3 chapters.
 * See https://id3.org/id3v2-chapters-1.0
 */
public class ChapterReader extends ID3Reader {
    private static final String TAG = "ID3ChapterReader";

    public static final String FRAME_ID_CHAPTER = "CHAP";
    public static final String FRAME_ID_TITLE = "TIT2";
    public static final String FRAME_ID_LINK = "WXXX";
    public static final String FRAME_ID_PICTURE = "APIC";
    public static final String MIME_IMAGE_URL = "-->";
    public static final int IMAGE_TYPE_COVER = 3;

    private final List<Chapter> chapters = new ArrayList<>();

    public ChapterReader(CountingInputStream input) {
        super(input);
    }

    @Override
    protected void readFrame(@NonNull FrameHeader frameHeader) throws IOException, ID3ReaderException {
        if (FRAME_ID_CHAPTER.equals(frameHeader.getId())) {
            Log.d(TAG, "Handling frame: " + frameHeader.toString());
            Chapter chapter = readChapter(frameHeader);
            Log.d(TAG, "Chapter done: " + chapter);
            chapters.add(chapter);
        } else {
            super.readFrame(frameHeader);
        }
    }

    public Chapter readChapter(@NonNull FrameHeader frameHeader) throws IOException, ID3ReaderException {
        int chapterStartedPosition = getPosition();
        String elementId = readIsoStringNullTerminated(100);
        long startTime = readInt();
        skipBytes(12); // Ignore end time, start offset, end offset

        Chapter chapter = new Chapter();
        chapter.setStart(startTime);
        chapter.setChapterId(elementId);

        // Read sub-frames
        while (getPosition() < chapterStartedPosition + frameHeader.getSize()) {
            FrameHeader subFrameHeader = readFrameHeader();
            readChapterSubFrame(subFrameHeader, chapter);
        }
        return chapter;
    }

    public void readChapterSubFrame(@NonNull FrameHeader frameHeader, @NonNull Chapter chapter)
            throws IOException, ID3ReaderException {
        Log.d(TAG, "Handling subframe: " + frameHeader.toString());
        int frameStartPosition = getPosition();
        switch (frameHeader.getId()) {
            case FRAME_ID_TITLE:
                chapter.setTitle(readEncodingAndString(frameHeader.getSize()));
                Log.d(TAG, "Found title: " + chapter.getTitle());
                break;
            case FRAME_ID_LINK:
                readEncodingAndString(frameHeader.getSize()); // skip description
                String url = readIsoStringNullTerminated(frameStartPosition + frameHeader.getSize() - getPosition());
                try {
                    String decodedLink = URLDecoder.decode(url, "ISO-8859-1");
                    chapter.setLink(decodedLink);
                    Log.d(TAG, "Found link: " + chapter.getLink());
                } catch (IllegalArgumentException iae) {
                    Log.w(TAG, "Bad URL found in ID3 data");
                }
                break;
            case FRAME_ID_PICTURE:
                byte encoding = readByte();
                String mime = readIsoStringNullTerminated(frameHeader.getSize());
                byte type = readByte();
                String description = readEncodedString(encoding, frameHeader.getSize());
                Log.d(TAG, "Found apic: " + mime + "," + description);
                if (MIME_IMAGE_URL.equals(mime)) {
                    String link = readIsoStringNullTerminated(frameHeader.getSize());
                    Log.d(TAG, "Link: " + link);
                    if (TextUtils.isEmpty(chapter.getImageUrl()) || type == IMAGE_TYPE_COVER) {
                        chapter.setImageUrl(link);
                    }
                } else {
                    int alreadyConsumed = getPosition() - frameStartPosition;
                    int rawImageDataLength = frameHeader.getSize() - alreadyConsumed;
                    if (TextUtils.isEmpty(chapter.getImageUrl()) || type == IMAGE_TYPE_COVER) {
                        chapter.setImageUrl(EmbeddedChapterImage.makeUrl(getPosition(), rawImageDataLength));
                    }
                }
                break;
            default:
                Log.d(TAG, "Unknown chapter sub-frame.");
                break;
        }

        // Skip garbage to fill frame completely
        // This also asserts that we are not reading too many bytes from this frame.
        int alreadyConsumed = getPosition() - frameStartPosition;
        skipBytes(frameHeader.getSize() - alreadyConsumed);
    }

    public List<Chapter> getChapters() {
        return chapters;
    }

}
