package com.badoo.hprof.cruncher;

import com.badoo.hprof.cruncher.bmd.BmdTag;
import com.badoo.hprof.cruncher.bmd.DataWriter;
import com.badoo.hprof.library.HprofReader;
import com.badoo.hprof.library.Tag;
import com.badoo.hprof.library.model.HprofString;
import com.badoo.hprof.library.processor.DiscardProcessor;
import com.badoo.hprof.library.util.StreamUtil;
import com.sun.istack.internal.Nullable;

import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Erik Andre on 22/10/14.
 */
public class CrunchProcessor extends DiscardProcessor {

    private class CrunchBdmWriter extends DataWriter {

        protected CrunchBdmWriter(OutputStream out) {
            super(out);
        }

        public void writeHeader(int version, @Nullable byte[] metadata) throws IOException {
            writeInt32(version);
            writeByteArrayWithLength(metadata != null? metadata : new byte[]{});
        }

        public void writeString(HprofString string, boolean hashed) throws IOException {
            writeInt32(hashed? BmdTag.HASHED_STRING : BmdTag.STRING);
            writeInt32(string.getId());
            if (hashed) {
                writeInt32(string.getValue().hashCode());
            } else {
                writeByteArrayWithLength(string.getValue().getBytes());
            }
        }

        public void writeLegacyRecord(int tag, byte[] data) throws IOException {
            writeInt32(BmdTag.LEGACY_HPROF_RECORD);
            writeInt32(tag);
            writeRawBytes(data);
        }
    }

    private final CrunchBdmWriter writer;
    private int nextStringId;
    private Map<Integer, Integer> stringIds = new HashMap<Integer, Integer>(); // Maps original to updated string ids

    public CrunchProcessor(OutputStream out) {
        this.writer = new CrunchBdmWriter(out);
    }

    @Override
    public void onRecord(int tag, int timestamp, int length, HprofReader reader) throws IOException {
        switch (tag) {
            case Tag.STRING:
                HprofString string = reader.readStringRecord(length, timestamp);
                // We replace the original string id with one starting from 0 as these are more efficient to store
                stringIds.put(string.getId(), nextStringId); // Save the original id so we can update references later
                string.setId(nextStringId);
                nextStringId++;
                writer.writeString(string, true);
                break;
            case Tag.HEAP_DUMP:
            case Tag.HEAP_DUMP_SEGMENT:
                // TODO Process
                super.onRecord(tag, timestamp, length, reader);
                break;
            case Tag.UNLOAD_CLASS:
            case Tag.HEAP_DUMP_END:
                super.onRecord(tag, timestamp, length, reader); // These records can be discarded
                break;
            default:
                byte[] data = StreamUtil.read(reader.getInputStream(), length);
                writer.writeLegacyRecord(tag, data);
                break;
        }
    }

    @Override
    public void onHeader(String text, int idSize, int timeHigh, int timeLow) throws IOException {
        writer.writeHeader(1, text.getBytes());
    }
}

@@ -196,24 +196,302 @@
 ibrary.Tag;%0A
+import com.badoo.hprof.library.heap.HeapDumpProcessor;%0Aimport com.badoo.hprof.library.heap.HeapDumpReader;%0Aimport com.badoo.hprof.library.heap.HeapTag;%0Aimport com.badoo.hprof.library.heap.processor.HeapDumpDiscardProcessor;%0Aimport com.badoo.hprof.library.model.ClassDefinition;%0A
 import com.b
@@ -1858,24 +1858,562 @@
    %7D%0A    %7D%0A%0A
+    private class ClassDumpProcessor extends HeapDumpDiscardProcessor %7B%0A%0A        @Override%0A        public void onHeapRecord(int tag, HeapDumpReader reader) throws IOException %7B%0A            switch (tag) %7B%0A                case HeapTag.CLASS_DUMP:%0A                    ClassDefinition def = reader.readClassDumpRecord(classes);%0A                    System.out.println(%22Reading class %22 + def.getObjectId());%0A                    break;%0A                default:%0A                    super.onHeapRecord(tag, reader);%0A            %7D%0A        %7D%0A    %7D%0A%0A
     private 
@@ -2589,16 +2589,109 @@
 ring ids
+%0A    private Map%3CInteger, ClassDefinition%3E classes = new HashMap%3CInteger, ClassDefinition%3E();
 %0A%0A    pu
@@ -3410,32 +3410,224 @@
          break;%0A
+            case Tag.LOAD_CLASS:%0A                ClassDefinition classDef = reader.readLoadClassRecord();%0A                classes.put(classDef.getObjectId(), classDef);%0A                break;%0A
             case
@@ -3702,23 +3702,68 @@
     
-// TODO 
+ClassDumpProcessor dumpProcessor = new ClassDump
 Process
+or();
 %0A   
@@ -3779,55 +3779,207 @@
     
-super.onRecord(tag, timestamp, length, reader);
+HeapDumpReader dumpReader = new HeapDumpReader(reader.getInputStream(), length, dumpProcessor);%0A                while (dumpReader.hasNext()) %7B%0A                    dumpReader.next();%0A                %7D
 %0A   

Read class defs