/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEpdf 3.0 open source software code, released
 * May 1st, 2009. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2009 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */
package com.softwareimaging.jpdf.core.pobjects;

import com.softwareimaging.android.graphics.Color;
import com.softwareimaging.android.graphics.Image;
import com.softwareimaging.jpdf.core.pobjects.graphics.*;
import com.softwareimaging.jpdf.core.util.Library;
import com.softwareimaging.jpdf.util.Hashtable;
import com.softwareimaging.logger.Logger;

import java.util.Enumeration;

/**
 * A resource is a dictionary type as defined by the PDF specification.  It
 * can contain fonts, xobjects, colorspaces, patterns, shading and
 * external graphic states.
 *
 * @since 1.0
 */
public class Resources extends Dictionary {

    Hashtable fonts;
    Hashtable xobjects;
    Hashtable colorspaces;
    Hashtable patterns;
    Hashtable shading;
    Hashtable extGStates;

    /**
     * @param l
     * @param h
     */
    public Resources(Library l, Hashtable h) {
        super(l, h);
        colorspaces = library.getDictionary(entries, "ColorSpace");
        fonts = library.getDictionary(entries, "Font");
        xobjects = library.getDictionary(entries, "XObject");
        patterns = library.getDictionary(entries, "Pattern");
        shading = library.getDictionary(entries, "Shading");
        extGStates = library.getDictionary(entries, "ExtGState");
    }

    public void dispose(boolean cache) {
      // NOTE: Make sure not to clear fonts, color spaces, pattern,
        // or extGStat's as this hold reverences to object not the actual
        // object. The only images contain object with a lot of memory

        if (xobjects != null) {
            Enumeration xobjectContent = xobjects.elements();
            while (xobjectContent.hasMoreElements()) {
                Object tmp = xobjectContent.nextElement();
                if (tmp instanceof Stream) {
                    Stream stream = (Stream) tmp;
                    stream.dispose(cache);
                }
                if (tmp instanceof Reference) {
                    Object reference = library.getObject((Reference) tmp);
                    if (reference instanceof Form) {
                        Form form = (Form) reference;
                        form.dispose(cache);
                    }
                    if (reference instanceof Stream) {
                        Stream stream = (Stream) reference;
                        stream.dispose(cache);
                    }
                }
            }
        }
    }

    /**
     * @param o
     * @return
     */
    public PColorSpace getColorSpace(Object o) {

        if (o == null) {
            return null;
        }

        Object tmp;
        // every resource has a color space entry and o can be tmp in it.
        if (colorspaces != null && colorspaces.get(o) != null) {
            tmp = colorspaces.get(o);
            PColorSpace cs = PColorSpace.getColorSpace(library, tmp);
            if (cs != null) {
                cs.init();
            }
            return cs;
        }
        // look for our name in the pattern dictionary
        if (patterns != null && patterns.get(o) != null) {
            tmp = patterns.get(o);
            PColorSpace cs = PColorSpace.getColorSpace(library, tmp);
            if (cs != null) {
                cs.init();
            }
            return cs;
        }

        // if its not in color spaces or pattern then its a plain old
        // named colour space.
        PColorSpace cs = PColorSpace.getColorSpace(library, o);
        if (cs != null) {
            cs.init();
        }
        return cs;

    }

    /**
     * @param s
     * @return
     */
    public com.softwareimaging.jpdf.core.pobjects.fonts.Font getFont(String s) {
        com.softwareimaging.jpdf.core.pobjects.fonts.Font font = null;
        if (fonts != null) {
            Object ob = fonts.get(s);
            // check to make sure the library contains a font
            if (ob instanceof com.softwareimaging.jpdf.core.pobjects.fonts.Font) {
                font = (com.softwareimaging.jpdf.core.pobjects.fonts.Font) ob;
            }
            // the default value is most likely Reference
            else if (ob instanceof Reference) {
                font = (com.softwareimaging.jpdf.core.pobjects.fonts.Font) library.getObject((Reference) ob);
            }
        }
        if (font != null) {
            font.init();
        }
        return font;
    }

    /**
     * @param s
     * @param fill
     * @return
     */
    public Image getImage(String s, Color fill) {
        // check image has for image
        Image image = null;

        // check xobjects for stream
        Stream st = (Stream) library.getObject(xobjects, s);
        if (st == null) {
            return null;
        }
        // return null if the xobject is not an image
        if (!st.isImageSubtype()) {
            return null;
        }
        // lastly return the images.
        try {
            image = st.getImage(fill, this);
        }
        catch (Exception e) {
        	if (Logger.areErrorsSupported()) {
        		Logger.outputError("Error getting image by name: " + s, e);
        	}
        }

        return image;
    }

    /**
     * @param s
     * @return
     */
    public boolean isForm(String s) {
        Object o = library.getObject(xobjects, s);
        return o instanceof Form;
    }

    /**
     * Gets the Form XObject specified by the named reference.
     *
     * @param nameReference name of resourse to retreive.
     * @return if the named reference is found return it, otherwise return null;
     */
    public Form getForm(String nameReference) {
        Form formXObject = null;
        Object tempForm = library.getObject(xobjects, nameReference);
        if (tempForm instanceof Form) {
            formXObject = (Form) tempForm;
        }
        return formXObject;
    }

    /**
     * Retrieves a Pattern object given the named resource.  This can be
     * call for a fill, text fill or do image mask.
     *
     * @param name of object to find.
     * @return tiling or shading type pattern object.  If not constructor is
     *         found, then null is returned.
     */
    public Pattern getPattern(String name) {
        if (patterns != null) {

            Object attribute = library.getObject(patterns, name);
            // An instance of TilingPattern will always have a stream
            if (attribute != null && attribute instanceof TilingPattern) {
                return (TilingPattern) attribute;
            }
            // ShaddingPatterns will not have a stream but still need to parsed
            else if (attribute != null && attribute instanceof Hashtable) {
                return ShadingPattern.getShadingPattern(library,
                        (Hashtable) attribute);
            }
        }
        return null;
    }

    /**
     * Gets the shadding pattern based on a shading dictionary name,  similar
     * to getPattern but is only called for the 'sh' token.
     *
     * @param name name of shading dictionary
     * @return associated shading pattern if any.
     */
    public ShadingPattern getShading(String name) {
        // look for pattern name in the shading dictionary, used by 'sh' tokens
        if (shading != null) {
            Object shadingDictionary = library.getObject(shading, name);
            if (shadingDictionary != null && shadingDictionary instanceof Hashtable) {
               try {
                  return ShadingPattern.getShadingPattern(library, entries,
                        (Hashtable) shadingDictionary);
               }
               catch (Exception e) {
                  // We don't support shading patterns yet - so shoudl see this occasionally
                  Logger.outputStackTrace(e);
               }
            }
//            else if (shadingDictionary != null && shadingDictionary instanceof Stream) {
//                System.out.println("Found Type 6 shading pattern.... returning empty pattern data. ");
            // todo: alter parser to take into account stream shading types...
//                return new ShadingType6Pattern(library, null);
//                return null;
//            }
        }
        return null;
    }

    /**
     * Returns the ExtGState object which has the specified reference name.
     *
     * @param namedReference name of ExtGState object to try and find.
     * @return ExtGState which contains the named references ExtGState attrbutes,
     *         if the namedReference could not be found null is returned.
     */
    public ExtGState getExtGState(String namedReference) {
        ExtGState gsState = null;
        if (extGStates != null) {
            Object attribute = library.getObject(extGStates, namedReference);
            if (attribute instanceof Hashtable) {
                gsState = new ExtGState(library, (Hashtable) attribute);
            } else if (attribute instanceof Reference) {
                gsState = new ExtGState(library,
                        (Hashtable) library.getObject(
                                (Reference) attribute));
            }
        }
        return gsState;

    }
}
