/**
 * Copyright 2015 Comcast Cable Communications Management, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.comcast.viper.hlsparserj.tags;

import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * UnparsedTag represents a generic tag in the playlist.
 */
public class UnparsedTag {

    private static final Pattern TAGPATTERN = Pattern.compile("^#(EXT.*?):(.*)");
    private static final String URI_ATTR = "URI";

    private String tagName;
    private Map<String, String> attributes;
    private String uri;
    private String rawTag;

    /**
     * Constructor.
     */
    public UnparsedTag() {
        attributes = new HashMap<String, String>();
    }

    /**
     * Constructor.
     * @param line playlist line item
     */
    public UnparsedTag(final String line) {
        rawTag = line;
        attributes = new HashMap<String, String>();
        parseTagLine(line);
    }

    /**
     * Return the tag name.
     * @return name
     */
    public String getTagName() {
        return tagName;
    }

    /**
     * Sets the tag name.
     * @param tagName tag name
     */
    public void setTagName(final String tagName) {
        this.tagName = tagName;
    }

    /**
     * Returns the list of attributes for this tag.
     * @return list of attributes
     */
    public Map<String, String> getAttributes() {
        return attributes;
    }

    /**
     * Sets the list of attributes for this tag.
     * @param attributes list of attributes
     */
    public void setAttributes(final Map<String, String> attributes) {
        this.attributes = attributes;
    }

    /**
     * Returns the URI for this tag.
     * @return URI
     */
    public String getURI() {
        return uri;
    }

    /**
     * Sets the URI for this tag.
     * @param uriString URI
     */
    public void setURI(final String uriString) {
        this.uri = uriString;
    }

    /**
     * Returns the raw tag string for the playlist line.
     * @return raw original tag string
     */
    public String getRawTag() {
        return rawTag;
    }

    /**
     * Parses the tag line.
     * @param line playlist line item
     */
    private void parseTagLine(final String line) {
        final Matcher lineMatcher = TAGPATTERN.matcher(line);

        // Create a matcher that uses the TAGPATTERN
        if (lineMatcher.find()) {
            tagName = lineMatcher.group(1);

            final String attributeList = lineMatcher.group(2);

            final StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(attributeList));
            tokenizer.resetSyntax();
            tokenizer.wordChars(' ', 255);
            tokenizer.quoteChar('"');
            tokenizer.ordinaryChar(',');
            tokenizer.ordinaryChar('=');

            String attributeName = null;
            String attributeValue = null;
            int noNameCount = 0;
            do {
                int ttype;
                try {
                    ttype = tokenizer.nextToken();
                } catch (IOException e) {
                    // Should never get here because reading from String
                    throw new IllegalStateException(e);
                }

                if (ttype == ',' || ttype == StreamTokenizer.TT_EOF) {
                    if (attributeValue == null) {
                        // Not actually an attribute - just a single value
                        attributes.put("NONAME" + noNameCount, attributeName);
                        noNameCount++;
                        attributeName = null;
                    } else {
                        attributes.put(attributeName, attributeValue);
                        attributeName = null;
                        attributeValue = null;
                    }
                } else if (ttype == StreamTokenizer.TT_WORD || ttype == '"') {
                    if (attributeName == null) {
                        attributeName = tokenizer.sval;
                    } else {
                        attributeValue = tokenizer.sval;
                    }
                }
            } while (tokenizer.ttype != StreamTokenizer.TT_EOF);

            // Set the URI if a URI attribute is present
            if (attributes.containsKey(URI_ATTR)) {
                uri = attributes.get(URI_ATTR);
            }
        } else {
            // If the line startex with #EXT but does not contain a colon it is a
            // tag with no attributes
            tagName = line.substring(1);
        }
    }
}