/*
 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.tools.internal.xjc.util;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.activation.MimeType;
import javax.activation.MimeTypeParseException;

/**
 * @author Kohsuke Kawaguchi
 */
public class MimeTypeRange {
    public final String majorType;
    public final String subType;

    public final Map<String,String> parameters = new HashMap<String, String>();

    /**
     * Each media-range MAY be followed by one or more accept-params,
     * beginning with the "q" parameter for indicating a relative quality
     * factor. The first "q" parameter (if any) separates the media-range
     * parameter(s) from the accept-params. Quality factors allow the user
     * or user agent to indicate the relative degree of preference for that
     * media-range, using the qvalue scale from 0 to 1 (section 3.9). The
     * default value is q=1.
     */
    public final float q;

    // accept-extension is not implemented

    public static List<MimeTypeRange> parseRanges(String s) throws ParseException {
        StringCutter cutter = new StringCutter(s,true);
        List<MimeTypeRange> r = new ArrayList<MimeTypeRange>();
        while(cutter.length()>0) {
            r.add(new MimeTypeRange(cutter));
        }
        return r;
    }

    public MimeTypeRange(String s) throws ParseException {
        this(new StringCutter(s,true));
    }

    /**
     * Used only to produce the static constants within this class.
     */
    private static MimeTypeRange create(String s) {
        try {
            return new MimeTypeRange(s);
        } catch (ParseException e) {
            // we only use this method for known inputs
            throw new Error(e);
        }
    }

    /**
     * @param cutter
     *      A string like "text/html; charset=utf-8;
     */
    private MimeTypeRange(StringCutter cutter) throws ParseException {
        majorType = cutter.until("/");
        cutter.next("/");
        subType = cutter.until("[;,]");

        float q = 1.0f;

        while(cutter.length()>0) {
            String sep = cutter.next("[;,]");
            if(sep.equals(","))
                break;

            String key = cutter.until("=");
            cutter.next("=");
            String value;
            char ch = cutter.peek();
            if(ch=='"') {
                // quoted
                cutter.next("\"");
                value = cutter.until("\"");
                cutter.next("\"");
            } else {
                value = cutter.until("[;,]");
            }

            if(key.equals("q")) {
                q = Float.parseFloat(value);
            } else {
                parameters.put(key,value);
            }
        }

        this.q = q;
    }

    public MimeType toMimeType() throws MimeTypeParseException {
        // due to the additional error check done in the MimeType class,
        // an error at this point is possible
        return new MimeType(toString());
    }

    public String toString() {
        StringBuilder sb = new StringBuilder(majorType+'/'+subType);
        if(q!=1)
            sb.append("; q=").append(q);

        for( Map.Entry<String,String> p : parameters.entrySet() ) {
            // I'm too lazy to quote the value
            sb.append("; ").append(p.getKey()).append('=').append(p.getValue());
        }
        return sb.toString();
    }

    public static final MimeTypeRange ALL = create("*/*");

    /**
     * Creates a range by merging all the given types.
     */
    public static MimeTypeRange merge( Collection<MimeTypeRange> types ) {
        if(types.size()==0)     throw new IllegalArgumentException();
        if(types.size()==1)     return types.iterator().next();

        String majorType=null;
        for (MimeTypeRange mt : types) {
            if(majorType==null)     majorType = mt.majorType;
            if(!majorType.equals(mt.majorType))
                return ALL;
        }

        return create(majorType+"/*");
    }

    public static void main(String[] args) throws ParseException {
        for( MimeTypeRange m : parseRanges(args[0]))
            System.out.println(m.toString());
    }
}
