package gov.nasa.worldwind.formats.nitfs;

import gov.nasa.worldwind.*;
import gov.nasa.worldwind.formats.rpf.*;

import java.io.*;
import java.text.*;
/*
Copyright (C) 2001, 2007 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/

/**
 * @author Lado Garakanidze
 * @version $Id: NitfsMessage Apr 4, 2007 4:11:55 PM lado
 */
public class NitfsMessage
{
    private     java.nio.ByteBuffer     buffer;
    private     NitfsFileHeader         fileHeader;
    private     java.util.ArrayList<NitfsSegment> segments = new java.util.ArrayList<NitfsSegment>();


    public NitfsSegment getSegment( NitfsSegmentType segmentType )
    {
        for(NitfsSegment seg : segments)
        {
            if(null != seg && seg.segmentType.equals(segmentType))
                return seg;
        }
        return null;
    }

    public  NitfsFileHeader getNitfsFileHeader()
    {
        return this.fileHeader;
    }

    private NitfsMessage(java.nio.ByteBuffer buffer)
    {
        this.buffer = buffer;
        this.fileHeader = new NitfsFileHeader(buffer);

        // read ALL description groups and segments
        this.readSegments();
    }

    private void readSegments()
    {
        int saveOffset = this.buffer.position();
        int nextSegmentOffset = this.fileHeader.getHeaderLength();

        // parse Image Description Group
        nextSegmentOffset = parseSegment(NitfsSegmentType.ImageSegment, nextSegmentOffset);
        // parse Graphic/Symbol Description Group
        nextSegmentOffset = parseSegment(NitfsSegmentType.SymbolSegment, nextSegmentOffset);
        // parse Label Description Group
        nextSegmentOffset = parseSegment(NitfsSegmentType.LabelSegment, nextSegmentOffset);
        // parse Text Description Group
        nextSegmentOffset = parseSegment(NitfsSegmentType.TextSegment, nextSegmentOffset);
        // parse Data Extension Description Group
        nextSegmentOffset = parseSegment(NitfsSegmentType.DataExtensionSegment, nextSegmentOffset);
        // parse Reserved Extension Description Group
        nextSegmentOffset = parseSegment(NitfsSegmentType.ReservedExtensionSegment, nextSegmentOffset);
        // parse User Defined Header Description (UDHD) Group
        NitfsUserDefinedHeaderSegment userHeaderSeg = new RpfUserDefinedHeaderSegment(this.buffer);
        this.segments.add( userHeaderSeg );
        nextSegmentOffset += userHeaderSeg.headerLength + userHeaderSeg.dataLength;
        // parse Extended Header Description Group
        nextSegmentOffset = parseSegment(NitfsSegmentType.ExtendedHeaderSegment, nextSegmentOffset);

        // let's read each header
        for(NitfsSegment segment : segments)
        {

//
//            String segId = NitfsUtil.getString(buffer, segment.headerStartOffset, 2);
//            System.out.println("Segment type=" + segment.segmentType + ", id=" + segId);
        }
    }

    private int parseSegment(NitfsSegmentType segType, int nextSegmentOffset)
    {
        int headerLengthSize = segType.getHeaderLengthSize();
        int dataLengthSize = segType.getDataLengthSize();

        int numOfSegments = Integer.parseInt(NitfsUtil.getString(this.buffer, 3));
        for (int i = 0; i < numOfSegments; i++)
        {
            int segHeaderLength = Integer.parseInt(NitfsUtil.getString(this.buffer, headerLengthSize));
            int seqDataLength = Integer.parseInt(NitfsUtil.getString(this.buffer, dataLengthSize));

            int saveOffset = this.buffer.position(); // pass buffer to NitfsSegment to parse their headers' contents
            NitfsSegment segment;
            switch (segType)
            {
                case ImageSegment:
                    segment = new NitfsImageSegment(this.buffer, nextSegmentOffset, segHeaderLength,
                                        nextSegmentOffset + segHeaderLength, seqDataLength);
                    break;
                case SymbolSegment:
                    segment = new NitfsSymbolSegment(this.buffer, nextSegmentOffset, segHeaderLength,
                                        nextSegmentOffset + segHeaderLength, seqDataLength);
                    break;
                case LabelSegment:
                    segment = new NitfsLabelSegment(this.buffer, nextSegmentOffset, segHeaderLength,
                                        nextSegmentOffset + segHeaderLength, seqDataLength);
                    break;
                case TextSegment:
                    segment = new NitfsTextSegment(this.buffer, nextSegmentOffset, segHeaderLength,
                                        nextSegmentOffset + segHeaderLength, seqDataLength);
                    break;
                case DataExtensionSegment:
                    segment = new NitfsDataExtensionSegment(this.buffer, nextSegmentOffset, segHeaderLength,
                                        nextSegmentOffset + segHeaderLength, seqDataLength);
                    break;
                case ReservedExtensionSegment:
                    segment = new NitfsReservedExtensionSegment(this.buffer, nextSegmentOffset, segHeaderLength,
                                        nextSegmentOffset + segHeaderLength, seqDataLength);
                    break;
                case UserDefinedHeaderSegment:
                    segment = new RpfUserDefinedHeaderSegment(this.buffer);
                    break;
                case ExtendedHeaderSegment:    // // throw exception - wrong parser for ExtendedHeaderSegment
                    segment = new NitfsExtendedHeaderSegment(this.buffer, nextSegmentOffset, segHeaderLength,
                                        nextSegmentOffset + segHeaderLength, seqDataLength);
                    break;

                default:
                    throw new NitfsRuntimeException("NitfsReader.UnknownOrUnsupportedSegment", segType.toString());

            }
            this.segments.add(segment);

            nextSegmentOffset += segHeaderLength + seqDataLength;
            buffer.position(saveOffset); // restore offset
        }
        return nextSegmentOffset;
    }

    public static NitfsMessage load(java.io.File file) throws java.io.IOException
    {
        validateImageFile(file);

        java.nio.ByteBuffer roBuffer = NitfsUtil.readEntireFile(file).asReadOnlyBuffer();

        // check if it is a NITFS format file (NITF or NSIF - for NATO Secondary Imagery Format)
        String fmtId = NitfsUtil.getString(roBuffer, 0, 4);
        if( 0 != "NITF".compareTo(fmtId) && 0 != "NSIF".compareTo(fmtId))
        {
            roBuffer = null;
            throw new NitfsRuntimeException("NitfsReader.UnknownOrUnsupportedNitfsFormat", file.getCanonicalPath());
        }

        return new NitfsMessage(roBuffer);
    }

    private static void validateImageFile(java.io.File file)
        throws IOException, IllegalArgumentException, NitfsRuntimeException
    {
        if (null == file)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.FileIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }
        if (!file.exists() || !file.canRead())
            throw new NitfsRuntimeException("NitfsReader.NoFileOrNoPermission", file.getCanonicalPath());
    }

    public static void main(String args[])
    {
        String testImageFilename = "/depot/WorldWindJ/utils/gdal/020g222a.i42";

        if(Configuration.isWindowsOS())
            testImageFilename = "C:\\depot\\WorldWindJ\\utils\\gdal\\020g222a.i42";

        try
        {
            long startTime = System.currentTimeMillis();

            NitfsMessage img = NitfsMessage.load(new File(testImageFilename));

            System.out.println(MessageFormat.format("Image loaded in {0} mSec", (System.currentTimeMillis() - startTime)));

            System.out.println(img.fileHeader.getHeaderLength());
            System.out.println(img.fileHeader.getVersion());
            System.out.println(img.fileHeader.getComplexityLevel());
            System.out.println(img.fileHeader.getSpecialType());
            System.out.println(img.fileHeader.getOriginationStationId());
            System.out.println(img.fileHeader.getDateTime());
            System.out.println(img.fileHeader.getTitle());
            System.out.println(img.fileHeader.getFSCLAS());

            System.out.println("HeaderLength=" + img.fileHeader.getHeaderLength());
            System.out.println("Total FileLength=" + img.fileHeader.getFileLength());

            // System.out.println();
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }
}
