class PngModule extends ModuleBase {

    @Override
	public int parse (InputStream stream, RepInfo info, int parseIndex)
        throws IOException
    {
        initParse ();
        info.setFormat (_format[0]);
        info.setMimeType (_mimeType[0]);
        info.setModule (this);
        /* We may have already done the checksums while converting a
        temporary file. */
        _ckSummer = null;
        if (_je != null && _je.getChecksumFlag () &&
        		info.getChecksum ().size () == 0) {
        	_ckSummer = new Checksummer ();
        	_cstream = new ChecksumInputStream (stream, _ckSummer);
        	_dstream = getBufferedDataStream (_cstream, _je != null ?
                 _je.getBufferSize () : 0);
        }
        else {
        	_dstream = getBufferedDataStream (stream, _je != null ?
                 _je.getBufferSize () : 0);
        }
        _propList = new LinkedList<> ();
        _metadata = new Property ("PNGMetadata",
             PropertyType.PROPERTY,
             PropertyArity.LIST,
             _propList);
        _nisoData = new NisoImageMetadata();
        Property nisoProp = new Property(NISO_IMAGE_MD,
                PropertyType.NISOIMAGEMETADATA, _nisoData);
        _propList.add(nisoProp);
        _keywordPropList = new LinkedList<> ();
        _spltList = new LinkedList<> ();
        ErrorMessage msg;
        
        // Check that the file header matching the PNG magic numbers
        for (int i = 0; i < _sigBytes.length; i++) {
        	int byt = readUnsignedByte (_dstream);
        	if (byt != _sigBytes[i]) {
        		msg = new ErrorMessage(MessageConstants.PNG_GDM_66);
        		info.setMessage(msg);
        		info.setWellFormed(false);
        		return 0;
        	}
        }
        
        // Loop through the chunks
        try {
        	for (;;) {
        		PNGChunk chunk = readChunkHead(_dstream);
        		if (chunk == null) {
        			break;
        		}
        		if (iendSeen) {
        			msg = new ErrorMessage (MessageConstants.PNG_GDM_67);
        			info.setMessage (msg);
        			info.setWellFormed (false);
        			return 0;
        		}
        		if (idatSeen && !(chunk instanceof IdatChunk)) {
        			idatFinished = true;
        		}
        		chunk.setModule(this);
        		chunk.setInputStream(_dstream);
        		chunk.setNisoMetadata(_nisoData);
        		chunk.setPropertyList(_propList);
        		chunk.processChunk(info);
        		long storedCRC = chunk.readCRC();
        		long calculatedCRC = chunk.getCRC();
        		if (storedCRC != calculatedCRC) {
        			msg = new ErrorMessage(String.format (
							MessageConstants.PNG_GDM_68.getMessage() , 
							chunk.chunkTypeString()));
        			info.setMessage(msg);
        			info.setWellFormed(false);
        			return 0;
        		}
        	}
        }
        catch (PNGException e) {
        	// We've already reported a problem, so we just clean up here.
        	return 0;
        }
        catch (EOFException e) {
        	msg = new ErrorMessage (
        			String.format (
        					MessageConstants.PNG_GDM_69.getMessage(),
        					_nByte));
        	info.setMessage (msg);
        	info.setWellFormed (false);
        	return 0;
        }
        catch (Exception e) {
        	// Miscellaneous exceptions really shouldn't come here.
        	// But it's better to catch them than let them fall through.
        	// Treat them as bugs.
        	msg = new ErrorMessage (
        			String.format (
        					MessageConstants.PNG_GDM_70.getMessage() , 
        					e.getClass().getName()));
        	info.setMessage (msg);
        	info.setWellFormed (false);
        	return 0;
        }
        
        /* Check for required chunks. */
        boolean criticalMissing = false;
        if (!ihdrSeen) {
        	msg = new ErrorMessage(MessageConstants.PNG_GDM_71);
        	info.setMessage (msg);
        	criticalMissing = true;
        }
        if (!idatSeen) {
        	msg = new ErrorMessage(MessageConstants.PNG_GDM_72);
        	info.setMessage (msg);
        	criticalMissing = true;
        }
        if (!iendSeen) {
        	msg = new ErrorMessage(MessageConstants.PNG_GDM_73);
        	info.setMessage (msg);
        	criticalMissing = true;
        }
        if (criticalMissing) {
        	info.setWellFormed (false);
        	return 0;
		}
        
        /** PLTE is required with color type 3 and forbidden with types 0 and 4 */
        if (_colorType == 3 && !plteSeen) {
        	msg = new ErrorMessage (MessageConstants.PNG_GDM_74);
			info.setMessage (msg);
        	info.setWellFormed (false);
			return 0;
        }
        
        if ((_colorType == 0 || _colorType == 4) && plteSeen) {
			msg = new ErrorMessage (MessageConstants.PNG_GDM_75);
			info.setMessage (msg);
        	info.setWellFormed (false);
			return 0;
        }

        // Add the keyword property list if it isn't empty.
        if (!_keywordPropList.isEmpty()) {
        	_propList.add(new Property ("Keywords",
        			PropertyType.PROPERTY,
        			PropertyArity.LIST,
        			_keywordPropList));
        			
        }
        
        // Add the splat property list if it isn't empty.
        if (!_spltList.isEmpty()) {
        	_propList.add(new Property ("Suggested palettes",
        			PropertyType.PROPERTY,
        			PropertyArity.LIST,
        			_spltList));
        }
        info.setProperty (_metadata);
        return 0;
    }

    public  PngModule();

}