/*
 * @(#)JPEGImageMetadataFormat.java	1.12 03/12/19
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.imageio.plugins.jpeg;

import javax.imageio.metadata.IIOMetadataFormat;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.ImageTypeSpecifier;

import java.awt.color.ICC_Profile;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;

import java.util.List;
import java.util.ArrayList;

public class JPEGImageMetadataFormat extends JPEGMetadataFormat {

    private static JPEGImageMetadataFormat theInstance = null;

    private JPEGImageMetadataFormat() {
        super(JPEG.nativeImageMetadataFormatName,
              CHILD_POLICY_ALL);

        addElement("JPEGvariety", 
                   JPEG.nativeImageMetadataFormatName,
                   CHILD_POLICY_CHOICE);

        addElement("markerSequence", 
                   JPEG.nativeImageMetadataFormatName,
                   CHILD_POLICY_SEQUENCE);

        addElement("app0JFIF", "JPEGvariety", CHILD_POLICY_SOME);

        addStreamElements("markerSequence");

        addElement("app14Adobe", "markerSequence", CHILD_POLICY_EMPTY);

        addElement("sof", "markerSequence", 1, 4);

        addElement("sos", "markerSequence", 1, 4);

        addElement("JFXX", "app0JFIF", 1, Integer.MAX_VALUE);

        addElement("app0JFXX", "JFXX", CHILD_POLICY_CHOICE);

        addElement("app2ICC", "app0JFIF", CHILD_POLICY_EMPTY);

        addAttribute("app0JFIF", 
                     "majorVersion", 
                     DATATYPE_INTEGER,
                     false,
                     "1",
                     "0", "255",
                     true, true);
        addAttribute("app0JFIF", 
                     "minorVersion", 
                     DATATYPE_INTEGER,
                     false,
                     "2",
                     "0", "255",
                     true, true);
        List resUnits = new ArrayList();
        resUnits.add("0");
        resUnits.add("1");
        resUnits.add("2");
        addAttribute("app0JFIF", 
                     "resUnits",
                     DATATYPE_INTEGER,
                     false,
                     "0",
                     resUnits);
        addAttribute("app0JFIF", 
                     "Xdensity",
                     DATATYPE_INTEGER,
                     false,
                     "1",
                     "1", "65535",
                     true, true);
        addAttribute("app0JFIF", 
                     "Ydensity",
                     DATATYPE_INTEGER,
                     false,
                     "1",
                     "1", "65535",
                     true, true);
        addAttribute("app0JFIF", 
                     "thumbWidth",
                     DATATYPE_INTEGER,
                     false,
                     "0",
                     "0", "255",
                     true, true);
        addAttribute("app0JFIF", 
                     "thumbHeight",
                     DATATYPE_INTEGER,
                     false,
                     "0",
                     "0", "255",
                     true, true);

        addElement("JFIFthumbJPEG", "app0JFXX", CHILD_POLICY_SOME);
        addElement("JFIFthumbPalette", "app0JFXX", CHILD_POLICY_EMPTY);
        addElement("JFIFthumbRGB", "app0JFXX", CHILD_POLICY_EMPTY);

        List codes = new ArrayList();
        codes.add("16"); // Hex 10
        codes.add("17"); // Hex 11
        codes.add("19"); // Hex 13
        addAttribute("app0JFXX", 
                     "extensionCode", 
                     DATATYPE_INTEGER,
                     false,
                     null,
                     codes);

        addChildElement("markerSequence", "JFIFthumbJPEG");

        addAttribute("JFIFthumbPalette", 
                     "thumbWidth",
                     DATATYPE_INTEGER,
                     false,
                     null,
                     "0", "255",
                     true, true);
        addAttribute("JFIFthumbPalette", 
                     "thumbHeight",
                     DATATYPE_INTEGER,
                     false,
                     null,
                     "0", "255",
                     true, true);

        addAttribute("JFIFthumbRGB", 
                     "thumbWidth",
                     DATATYPE_INTEGER,
                     false,
                     null,
                     "0", "255",
                     true, true);
        addAttribute("JFIFthumbRGB", 
                     "thumbHeight",
                     DATATYPE_INTEGER,
                     false,
                     null,
                     "0", "255",
                     true, true);

        addObjectValue("app2ICC", ICC_Profile.class, false, null);

        addAttribute("app14Adobe", 
                     "version", 
                     DATATYPE_INTEGER,
                     false,
                     "100",
                     "100", "255",
                     true, true);
        addAttribute("app14Adobe", 
                     "flags0",
                     DATATYPE_INTEGER,
                     false,
                     "0",
                     "0", "65535",
                     true, true);
        addAttribute("app14Adobe", 
                     "flags1",
                     DATATYPE_INTEGER,
                     false,
                     "0",
                     "0", "65535",
                     true, true);

        List transforms = new ArrayList();
        transforms.add("0");
        transforms.add("1");
        transforms.add("2");
        addAttribute("app14Adobe", 
                     "transform",
                     DATATYPE_INTEGER,
                     true,
                     null,
                     transforms);

        addElement("componentSpec", "sof", CHILD_POLICY_EMPTY);

        List procs = new ArrayList();
        procs.add("0");
        procs.add("1");
        procs.add("2");
        addAttribute("sof", 
                     "process", 
                     DATATYPE_INTEGER,
                     false,
                     null,
                     procs);
        addAttribute("sof",
                     "samplePrecision",
                     DATATYPE_INTEGER,
                     false,
                     "8");
        addAttribute("sof",
                     "numLines",
                     DATATYPE_INTEGER,
                     false,
                     null,
                     "0", "65535",
                     true, true);
        addAttribute("sof",
                     "samplesPerLine",
                     DATATYPE_INTEGER,
                     false,
                     null,
                     "0", "65535",
                     true, true);
        List comps = new ArrayList();
        comps.add("1");
        comps.add("2");
        comps.add("3");
        comps.add("4");
        addAttribute("sof",
                     "numFrameComponents",
                     DATATYPE_INTEGER,
                     false,
                     null,
                     comps);

        addAttribute("componentSpec",
                     "componentId",
                     DATATYPE_INTEGER,
                     true,
                     null,
                     "0", "255",
                     true, true);
        addAttribute("componentSpec",
                     "HsamplingFactor",
                     DATATYPE_INTEGER,
                     true,
                     null,
                     "1", "255",
                     true, true);
        addAttribute("componentSpec",
                     "VsamplingFactor",
                     DATATYPE_INTEGER,
                     true,
                     null,
                     "1", "255",
                     true, true);
        List tabids = new ArrayList();
        tabids.add("0");
        tabids.add("1");
        tabids.add("2");
        tabids.add("3");
        addAttribute("componentSpec",
                     "QtableSelector",
                     DATATYPE_INTEGER,
                     true,
                     null,
                     tabids);

        addElement("scanComponentSpec", "sos", CHILD_POLICY_EMPTY);

        addAttribute("sos", 
                     "numScanComponents",
                     DATATYPE_INTEGER,
                     true,
                     null,
                     comps);
        addAttribute("sos",
                     "startSpectralSelection",
                      DATATYPE_INTEGER,
                     false,
                     "0",
                     "0", "63",
                     true, true);
        addAttribute("sos",
                     "endSpectralSelection",
                      DATATYPE_INTEGER,
                     false,
                     "63",
                     "0", "63",
                     true, true);
        addAttribute("sos",
                     "approxHigh",
                      DATATYPE_INTEGER,
                     false,
                     "0",
                     "0", "15",
                     true, true);
        addAttribute("sos",
                     "approxLow",
                      DATATYPE_INTEGER,
                     false,
                     "0",
                     "0", "15",
                     true, true);

        addAttribute("scanComponentSpec",
                     "componentSelector",
                     DATATYPE_INTEGER,
                     true,
                     null,
                     "0", "255",
                     true, true);
        addAttribute("scanComponentSpec",
                     "dcHuffTable",
                     DATATYPE_INTEGER,
                     true,
                     null,
                     tabids);
        addAttribute("scanComponentSpec",
                     "acHuffTable",
                     DATATYPE_INTEGER,
                     true,
                     null,
                     tabids);
    }

    public boolean canNodeAppear(String elementName,
                                 ImageTypeSpecifier imageType) {
        // All images can have these
        if (elementName.equals(getRootName())
            || elementName.equals("JPEGvariety")
            || isInSubtree(elementName, "markerSequence")) {
            return true;
        }

        // If it is an element in the app0jfif subtree, just check
        // that the image type is JFIF compliant.
        if ((isInSubtree(elementName, "app0JFIF")) 
            && JPEG.isJFIFcompliant(imageType, true)) {
            return true;
        }
        
        return false;
    }


    public static synchronized IIOMetadataFormat getInstance() {
        if (theInstance == null) {
            theInstance = new JPEGImageMetadataFormat();
        }
        return theInstance;
    }
}
