/*
 * Copyright (C) 2000 ymnk<ymnk@jcraft.com>
 *               2015 Trilarion
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.github.trilarion.sound.vorbis.jcraft.jorbis;

import java.util.logging.Logger;
import com.github.trilarion.sound.vorbis.jcraft.jogg.Buffer;
import com.github.trilarion.sound.vorbis.jcraft.jogg.Packet;

/**
 * The comments are not part of vorbis_info so that vorbis_info can be static
 * storage.
 */
public class Comment {

    private static final Logger LOG = Logger.getLogger(Comment.class.getName());

    private static final byte[] _vorbis = "vorbis".getBytes();
    private static final byte[] _vendor = "Xiphophorus libVorbis I 20000508".getBytes();

    private static final int OV_EIMPL = -130;

    // unlimited user comment fields.
    /**
     *
     */
    public byte[][] user_comments;

    /**
     *
     */
    public int[] comment_lengths;

    /**
     *
     */
    public int comments;

    /**
     *
     */
    public byte[] vendor;

    /**
     *
     */
    public void init() {
        user_comments = null;
        comments = 0;
        vendor = null;
    }

    /**
     *
     * @param comment
     */
    public void add(String comment) {
        add(comment.getBytes());
    }

    private void add(byte[] comment) {
        byte[][] foo = new byte[comments + 2][];
        if (user_comments != null) {
            System.arraycopy(user_comments, 0, foo, 0, comments);
        }
        user_comments = foo;

        int[] goo = new int[comments + 2];
        if (comment_lengths != null) {
            System.arraycopy(comment_lengths, 0, goo, 0, comments);
        }
        comment_lengths = goo;

        byte[] bar = new byte[comment.length + 1];
        System.arraycopy(comment, 0, bar, 0, comment.length);
        user_comments[comments] = bar;
        comment_lengths[comments] = comment.length;
        comments++;
        user_comments[comments] = null;
    }

    /**
     *
     * @param tag
     * @param contents
     */
    public void add_tag(String tag, String contents) {
        if (contents == null) {
            contents = "";
        }
        add(tag + "=" + contents);
    }

    static boolean tagcompare(byte[] s1, byte[] s2, int n) {
        int c = 0;
        byte u1, u2;
        while (c < n) {
            u1 = s1[c];
            u2 = s2[c];
            if ('Z' >= u1 && u1 >= 'A') {
                u1 = (byte) (u1 - 'A' + 'a');
            }
            if ('Z' >= u2 && u2 >= 'A') {
                u2 = (byte) (u2 - 'A' + 'a');
            }
            if (u1 != u2) {
                return false;
            }
            c++;
        }
        return true;
    }

    /**
     *
     * @param tag
     * @return
     */
    public String query(String tag) {
        return query(tag, 0);
    }

    /**
     *
     * @param tag
     * @param count
     * @return
     */
    public String query(String tag, int count) {
        int foo = query(tag.getBytes(), count);
        if (foo == -1) {
            return null;
        }
        byte[] comment = user_comments[foo];
        for (int i = 0; i < comment_lengths[foo]; i++) {
            if (comment[i] == '=') {
                return new String(comment, i + 1, comment_lengths[foo] - (i + 1));
            }
        }
        return null;
    }

    private int query(byte[] tag, int count) {
        int i = 0;
        int found = 0;
        int fulltaglen = tag.length + 1;
        byte[] fulltag = new byte[fulltaglen];
        System.arraycopy(tag, 0, fulltag, 0, tag.length);
        fulltag[tag.length] = (byte) '=';

        for (i = 0; i < comments; i++) {
            if (tagcompare(user_comments[i], fulltag, fulltaglen)) {
                if (count == found) {
                    // We return a pointer to the data, not a copy
                    //return user_comments[i] + taglen + 1;
                    return i;
                } else {
                    found++;
                }
            }
        }
        return -1;
    }

    int unpack(Buffer opb) {
        int vendorlen = opb.read(32);
        if (vendorlen < 0) {
            clear();
            return (-1);
        }
        vendor = new byte[vendorlen + 1];
        opb.read(vendor, vendorlen);
        comments = opb.read(32);
        if (comments < 0) {
            clear();
            return (-1);
        }
        user_comments = new byte[comments + 1][];
        comment_lengths = new int[comments + 1];

        for (int i = 0; i < comments; i++) {
            int len = opb.read(32);
            if (len < 0) {
                clear();
                return (-1);
            }
            comment_lengths[i] = len;
            user_comments[i] = new byte[len + 1];
            opb.read(user_comments[i], len);
        }
        if (opb.read(1) != 1) {
            clear();
            return (-1);

        }
        return (0);
    }

    int pack(Buffer opb) {
        // preamble
        opb.write(0x03, 8);
        opb.write(_vorbis);

        // vendor
        opb.write(_vendor.length, 32);
        opb.write(_vendor);

        // comments
        opb.write(comments, 32);
        if (comments != 0) {
            for (int i = 0; i < comments; i++) {
                if (user_comments[i] != null) {
                    opb.write(comment_lengths[i], 32);
                    opb.write(user_comments[i]);
                } else {
                    opb.write(0, 32);
                }
            }
        }
        opb.write(1, 1);
        return (0);
    }

    /**
     *
     * @param op
     * @return
     */
    public int header_out(Packet op) {
        Buffer opb = new Buffer();
        opb.writeinit();

        if (pack(opb) != 0) {
            return OV_EIMPL;
        }

        op.packet_base = new byte[opb.bytes()];
        op.packet = 0;
        op.bytes = opb.bytes();
        System.arraycopy(opb.buffer(), 0, op.packet_base, 0, op.bytes);
        op.b_o_s = 0;
        op.e_o_s = 0;
        op.granulepos = 0;
        return 0;
    }

    void clear() {
        for (int i = 0; i < comments; i++) {
            user_comments[i] = null;
        }
        user_comments = null;
        vendor = null;
    }

    /**
     *
     * @return
     */
    public String getVendor() {
        return new String(vendor, 0, vendor.length - 1);
    }

    /**
     *
     * @param i
     * @return
     */
    public String getComment(int i) {
        if (comments <= i) {
            return null;
        }
        return new String(user_comments[i], 0, user_comments[i].length - 1);
    }

    @Override
    public String toString() {
        String foo = "Vendor: " + new String(vendor, 0, vendor.length - 1);
        for (int i = 0; i < comments; i++) {
            foo = foo + "\nComment: "
                    + new String(user_comments[i], 0, user_comments[i].length - 1);
        }
        foo = foo + "\n";
        return foo;
    }
}
