package clarity.decoder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import clarity.match.DTClassCollection;
import clarity.model.PropFlag;
import clarity.model.PropType;
import clarity.model.ReceiveProp;
import clarity.model.SendProp;
import clarity.model.SendTable;
import clarity.model.SendTableExclusion;

public class SendTableFlattener {

    private final DTClassCollection lookup;
    private final SendTable descendant;
    private final Set<SendTableExclusion> exclusions;
    private final List<ReceiveProp> receiveProps;

    public SendTableFlattener(DTClassCollection lookup, SendTable descendant) {
        this.lookup = lookup;
        this.descendant = descendant;
        this.exclusions = aggregateExclusions(descendant);
        this.receiveProps = new LinkedList<ReceiveProp>();
    }

    private Set<SendTableExclusion> aggregateExclusions(SendTable table) {
        Set<SendTableExclusion> result = table.getAllExclusions();
        for (SendProp sp : table.getAllRelations()) {
            result.addAll(aggregateExclusions(lookup.sendTableForDtName(sp.getDtName())));
        }
        return result;
    }

    private void _flatten(SendTable ancestor, List<SendProp> accumulator, String proxy) {
        _flattenCollapsible(ancestor, accumulator);
        for (SendProp sp : accumulator) {
            String n = sp.getVarName();
            String s = sp.getSrc();
            if (proxy != null) {
                n = s + "." + n;
                s = proxy;
            }
            receiveProps.add(new ReceiveProp(sp, s, n));
        }
    }

    private void _flattenCollapsible(SendTable ancestor, List<SendProp> accumulator) {
        for (SendProp sp : ancestor.getAllNonExclusions()) {
            boolean excluded = exclusions.contains(new SendTableExclusion(ancestor.getNetTableName(), sp.getVarName()));
            boolean ineligible = (sp.isFlagSet(PropFlag.INSIDE_ARRAY));
            if (excluded || ineligible) {
                continue;
            }
            if (sp.getType() == PropType.DATATABLE) {
                if (sp.isFlagSet(PropFlag.COLLAPSIBLE)) {
                    _flattenCollapsible(lookup.sendTableForDtName(sp.getDtName()), accumulator);
                } else {
                    _flatten(lookup.sendTableForDtName(sp.getDtName()), new LinkedList<SendProp>(), ancestor.getNetTableName());
                }
            } else {
                accumulator.add(sp);
            }
        }
    }

    private List<ReceiveProp> sort() {
        List<ReceiveProp> sorted = new ArrayList<ReceiveProp>(receiveProps);
        Set<Integer> priorities = new TreeSet<Integer>();
        priorities.add(64);
        for (ReceiveProp rp : sorted) {
            priorities.add(rp.getPriority());
        }
        int offset = 0;

        for (Integer priority : priorities) {
            int hole = offset;
            int cursor = offset;
            while (cursor < sorted.size()) {
                ReceiveProp rp = sorted.get(cursor);
                boolean changesOften = rp.isFlagSet(PropFlag.CHANGES_OFTEN) && priority == 64;
                if (changesOften || rp.getPriority() == priority) {
                    Collections.swap(sorted, cursor, hole);
                    hole++;
                    offset++;
                }
                cursor++;
            }
        }
        return sorted;
    }

    public List<ReceiveProp> flatten() {
        _flatten(descendant, new LinkedList<SendProp>(), null);
        return sort();
    }
}
@@ -81,16 +81,41 @@
 tions;%0D%0A
+import java.util.Deque;%0D%0A
 import j
@@ -689,16 +689,57 @@
 eProps;%0D
+%0A    private final StringBuffer nameBuf;%0D
 %0A%0D%0A    p
@@ -1000,24 +1000,68 @@
 veProp%3E();%0D%0A
+        this.nameBuf = new StringBuffer();%0D%0A
     %7D%0D%0A%0D%0A   
@@ -1470,20 +1470,38 @@
 or, 
+Deque%3C
 String
+%3E
  p
-roxy
+ath, String src
 ) %7B%0D
@@ -1542,32 +1542,43 @@
 tor, accumulator
+, path, src
 );%0D%0A        for 
@@ -1577,53 +1577,39 @@
     
-for (SendProp sp : accumulator) %7B%0D%0A  
+nameBuf.setLength(0);%0D%0A
         
   St
@@ -1608,108 +1608,103 @@
     
-  
+for (
 String 
-n = sp.getVarName();%0D%0A            String s = sp.getSrc();%0D%0A            if (proxy != null) %7B
+part : path) %7B%0D%0A            nameBuf.append(part);%0D%0A            nameBuf.append('.');
 %0D%0A  
@@ -1709,24 +1709,27 @@
         
+%7D%0D%0A
         
 n = s + 
@@ -1724,123 +1724,260 @@
     
-n = s + %22.%22 + n;%0D%0A                s = proxy;%0D%0A            %7D%0D%0A            receiveProps.add(new ReceiveProp(sp, s, n)
+int l = nameBuf.length();%0D%0A        for (SendProp sp : accumulator) %7B%0D%0A            nameBuf.append(sp.getVarName());%0D%0A            receiveProps.add(new ReceiveProp(sp, src == null ? sp.getSrc() : src, nameBuf.toString()));%0D%0A            nameBuf.setLength(l
 );%0D%0A
@@ -2071,32 +2071,64 @@
 rop%3E accumulator
+, Deque%3CString%3E path, String src
 ) %7B%0D%0A        for
@@ -2666,16 +2666,27 @@
 umulator
+, path, src
 );%0D%0A    
@@ -2699,32 +2699,86 @@
       %7D else %7B%0D%0A
+                    path.offerLast(sp.getVarName());%0D%0A
                 
@@ -2860,16 +2860,36 @@
 Prop%3E(),
+ path, src == null ?
  ancesto
@@ -2903,24 +2903,70 @@
 tTableName()
+ : src);%0D%0A                    path.removeLast(
 );%0D%0A        
@@ -4105,16 +4105,42 @@
 Prop%3E(),
+ new LinkedList%3CString%3E(),
  null);%0D

fix flattening code to correctly handle nested incollapsible tables