package skadistats.clarity.processor.stringtables;

import com.google.protobuf.ByteString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import skadistats.clarity.event.Event;
import skadistats.clarity.event.EventListener;
import skadistats.clarity.event.Initializer;
import skadistats.clarity.event.UsagePoint;
import skadistats.clarity.model.StringTable;
import skadistats.clarity.processor.reader.OnReset;
import skadistats.clarity.processor.reader.ResetPhase;
import skadistats.clarity.processor.runner.Context;
import skadistats.clarity.util.Predicate;
import skadistats.clarity.wire.common.proto.Demo;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class BaseStringTableEmitter {

    protected static final int MAX_NAME_LENGTH = 0x400;
    protected static final int KEY_HISTORY_SIZE = 32;

    protected final Logger log = LoggerFactory.getLogger(getClass());

    protected int numTables = 0;

    private Set<String> requestedTables = new HashSet<>();
    private Set<String> updateEventTables = new HashSet<>();

    private final Map<String, Demo.CDemoStringTables.table_t> resetStringTables = new TreeMap<>();

    private Event<OnStringTableEntry> updateEvent = null;

    @Initializer(UsesStringTable.class)
    public void initStringTableUsage(final Context ctx, final UsagePoint<UsesStringTable> usagePoint) {
        requestedTables.add(usagePoint.getAnnotation().value());
    }

    @Initializer(OnStringTableEntry.class)
    public void initStringTableEntryEvent(final Context ctx, final EventListener<OnStringTableEntry> eventListener) {
        final String tableName = eventListener.getAnnotation().value();
        requestedTables.add(tableName);
        if ("*".equals(tableName)) {
            updateEventTables = requestedTables;
        } else {
            updateEventTables.add(tableName);
        }
        updateEvent = ctx.createEvent(OnStringTableEntry.class, StringTable.class, int.class, String.class, ByteString.class);
        eventListener.setInvocationPredicate(new Predicate<Object[]>() {
            @Override
            public boolean apply(Object[] args) {
                StringTable t = (StringTable) args[0];
                return "*".equals(tableName) || t.getName().equals(tableName);
            }
        });
    }

    protected boolean isProcessed(String tableName) {
        return requestedTables.contains("*") || requestedTables.contains(tableName);
    }

    protected void setSingleEntry(Context ctx, StringTable table, int mode, int index, String key, ByteString value) {
        table.set(mode, index, key, value);
        raise(table, index, key, value);
    }

    protected void raise(StringTable table, int index, String key, ByteString value) {
        if (updateEvent != null) {
            updateEvent.raise(table, index, key, value);
        }
    }

    @OnReset
    public void onReset(Context ctx, Demo.CDemoFullPacket packet, ResetPhase phase) {
        StringTables stringTables = ctx.getProcessor(StringTables.class);
        if (phase == ResetPhase.CLEAR) {
            resetStringTables.clear();
            for (StringTable table : stringTables.byName.values()) {
                table.reset();
            }
        } else if (phase == ResetPhase.STRINGTABLE_ACCUMULATION) {
            for (Demo.CDemoStringTables.table_t tt : packet.getStringTable().getTablesList()) {
                if (!stringTables.byName.containsKey(tt.getTableName())) {
                    continue;
                }
                resetStringTables.put(tt.getTableName(), tt);
            }
        } else if (phase == ResetPhase.STRINGTABLE_APPLY) {
            for (StringTable table : stringTables.byName.values()) {
                Demo.CDemoStringTables.table_t tt = resetStringTables.get(table.getName());
                if (tt != null) {
                    for (int i = 0; i < tt.getItemsCount(); i++) {
                        Demo.CDemoStringTables.items_t it = tt.getItems(i);
                        setSingleEntry(ctx, table, 2, i, it.getStr(), it.getData());
                    }
                } else {
                    for (int i = 0; i < table.getEntryCount(); i++) {
                        raise(table, i, table.getNameByIndex(i), table.getValueByIndex(i));
                    }
                }
            }
        }
    }

}

@@ -361,16 +361,70 @@
 gTable;%0A
+import skadistats.clarity.processor.reader.OnMessage;%0A
 import s
@@ -4431,10 +4431,667 @@
 %0A    %7D%0A%0A
+    @OnMessage(Demo.CDemoStringTables.class)%0A    public void onStringTables(Context ctx, Demo.CDemoStringTables packet) %7B%0A        StringTables stringTables = ctx.getProcessor(StringTables.class);%0A        for (Demo.CDemoStringTables.table_t tt : packet.getTablesList()) %7B%0A            StringTable table = stringTables.byName.get(tt.getTableName());%0A            if (table == null) %7B%0A                continue;%0A            %7D%0A            for (int i = 0; i %3C tt.getItemsCount(); i++) %7B%0A                Demo.CDemoStringTables.items_t it = tt.getItems(i);%0A                setSingleEntry(ctx, table, 2, i, it.getStr(), it.getData());%0A            %7D%0A        %7D%0A%0A    %7D%0A%0A%0A
 %7D%0A

allow reading console recorded