package com.timepath.hl2.io.bsp;

import com.timepath.io.OrderedInputStream;
import com.timepath.io.struct.StructField;
import java.io.*;
import java.nio.ByteOrder;
import java.util.logging.Logger;

/**
 *
 * https://developer.valvesoftware.com/wiki/Source_BSP_File_Format
 * https://github.com/TimePath/webgl-source/blob/master/js/source-bsp.js
 * https://github.com/TimePath/webgl-source/blob/master/js/source-bsp-struct.js
 * https://github.com/TimePath/webgl-source/blob/master/js/source-bsp-tree.js
 * <p/>
 * @author TimePath
 */
public class BSP {

    private static final Logger LOG = Logger.getLogger(BSP.class.getName());

    public static BSP load(InputStream is) throws IOException, InstantiationException, IllegalAccessException {
        OrderedInputStream in = new OrderedInputStream(new BufferedInputStream(is));
        in.order(ByteOrder.LITTLE_ENDIAN);
        in.mark(in.available());
        BSPHeader header = in.readStruct(new BSPHeader());

        // TODO: Other BSP types
        VBSP bsp = new VBSP();
        bsp.in = in;
        bsp.header = header;

        // TODO: Struct parser callbacks
        for(int i = 0; i < header.lumps.length; i++) {
            header.lumps[i].type = LumpType.values()[i];
        }
        return bsp;
    }

    protected BSPHeader header;

    protected OrderedInputStream in;

    protected BSP() {
    }

    /**
     *
     * Examples:
     * <br/>
     * {@code String ents = b.<String>getLump(LumpType.LUMP_ENTITIES);}
     * <br/>
     * {@code String ents = (String) b.getLump(LumpType.LUMP_ENTITIES);}
     * <br/>
     * {@code String ents = b.getLump(LumpType.LUMP_ENTITIES);}
     * <p/>
     * @param <T>  Expected return type. TODO: Wouldn't it be nice if we just knew?
     * @param type The lump
     * <p/>
     * @return The lump
     * <p/>
     * @throws IOException
     */
    public <T> T getLump(LumpType type) throws IOException {
        Lump lump = header.lumps[type.id];
        if(lump.isEmpty()) {
            return null;
        }
        in.reset();
        in.skipBytes(lump.offset);
        return type.<T>handle(lump, in);
    }

    /**
     *
     * @return The map revision
     */
    public int getRevision() {
        return header.mapRevision;
    }

    private static class BSPHeader {

        /**
         * BSP file identifier: VBSP
         */
        @StructField(index = 0)
        int ident;

        /**
         * BSP file identifier: VBSP
         */
        @StructField(index = 2)
        Lump[] lumps = new Lump[64];

        /**
         * The map's revision (iteration, version) number
         */
        @StructField(index = 3)
        int mapRevision;

        /**
         * BSP file version
         */
        @StructField(index = 1)
        int version;

    }

}

@@ -1739,16 +1739,32 @@
 ust knew
+ at compile time
 ?%0A     *
@@ -1860,32 +1860,67 @@
 ception%0A     */%0A
+    @SuppressWarnings(%22unchecked%22)%0A
     public %3CT%3E T
@@ -1976,329 +1976,774 @@
     
-Lump lump = header.lumps%5Btype.id%5D
+return this.%3CT%3EgetLump(type, (LumpHandler%3CT%3E) type.handler)
 ;%0A    
+%7D%0A%0A
     
-if(lump.isEmpty()) %7B%0A            return null
+/**%0A     *%0A     * @return The map revision%0A     */%0A    public int getRevision() %7B%0A        return header.mapRevision
 ;%0A    
+%7D%0A%0A
     
-%7D
+/**
 %0A     
-   in.reset();%0A        in.skipBytes(lump.offset);%0A        return type.
+* Intended for overriding to change handler functionality%0A     * %3Cp/%3E%0A     * @param %3CT%3E%0A     * @param type%0A     * @param handler%0A     * %3Cp/%3E%0A     * @return%0A     * %3Cp/%3E%0A     * @throws IOException%0A     */%0A    protected %3CT%3E T getLump(LumpType type, LumpHandler
 %3CT%3E
+ 
 handle
-(lump, in);
+r) throws IOException %7B
 %0A    
-%7D%0A%0A
     
-/**
+if(handler == null) %7B
 %0A     
-*%0A
+ 
      
-* @
+ 
 return 
-The map revision%0A     */%0A    public int getRevision() %7B%0A        return header.mapRevision
+null;%0A        %7D%0A        Lump lump = header.lumps%5Btype.id%5D;%0A        if(lump.isEmpty()) %7B%0A            return null;%0A        %7D%0A        in.reset();%0A        in.skipBytes(lump.offset);%0A        return handler.handle(lump, in)
 ;%0A  

Allow overriding