package com.ibm.nmon.parser;

import org.slf4j.Logger;

import java.util.List;
import java.util.Map;

import java.io.IOException;

import java.io.File;

import java.io.BufferedReader;
import java.io.FileReader;

import java.io.StringReader;
import java.io.StringWriter;

import java.util.TimeZone;

import com.ibm.nmon.data.NMONDataSet;

/**
 * A parser for <code>topas -a</code> output. This serves as a bridge between Topas and NMON. This class takes the
 * output and converts it to a string that can be parsed by {@link NMONParser}.
 */
public final class TopasOutParser {
    private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(TopasOutParser.class);

    private final NMONParser nmonParser;

    public TopasOutParser(NMONParser nmonParser) {
        this.nmonParser = nmonParser;
    }

    // topas -a outputs sorted data; unsort it by looking for the header records and outputting them first
    // once the ZZZZ records are parsed output them then all the corresponding data records for that timestamp
    public NMONDataSet parse(String filename, TimeZone timeZone, boolean scaleProcessesByCPU) throws IOException {
        File file = new File(filename);

        BufferedReader in = null;

        StringWriter writer = null;

        try {
            in = new BufferedReader(new FileReader(file));
            writer = new StringWriter((int) file.length());

            // LinkedHashMap so insertion order is maintained; removes need to reparse headers later
            Map<String, List<String>> headersToLines = new java.util.LinkedHashMap<String, List<String>>(32);

            List<String> timestamps = new java.util.ArrayList<String>(128);

            String line = null;

            while ((line = in.readLine()) != null) {
                int idx = line.indexOf(",");

                String header = line.substring(0, idx);

                if ("AAA".equals(header)) {
                    // put all the AAA records at the top
                    writer.write(line);
                    writer.write('\n');
                }
                else if ("ZZZZ".equals(header)) {
                    timestamps.add(line);
                }
                else {
                    // save all the data by header name
                    List<String> lines = headersToLines.get(header);

                    if (lines == null) {
                        LOGGER.trace("found {} " + "header", header);
                        lines = new java.util.ArrayList<String>(218);
                        headersToLines.put(header, lines);
                    }

                    if (line.charAt(idx + 1) == 'T') {
                        lines.add(line);
                    }
                    else {
                        if ("LPAR".equals(header)) {
                            line = "LPAR,LPAR Stats,PhysicalCPU,virtualCPUs,logicalCPUs,poolCPUs,entitled,weight,PoolIdle,usedAllCPU%,usedPoolCPU%,SharedCPU";
                        }

                        writer.write(line);
                        writer.write('\n');
                    }
                }
            }

            LOGGER.debug("found {} " + "ZZZZ timestamps", timestamps.size());
            LOGGER.debug("found {} " + "data types", headersToLines.size());

            // output each timestamp, then all the data for that time
            // assume all arrays are the same size, i.e. there is data at each timestamp for all values
            for (int i = 0; i < timestamps.size(); i++) {
                writer.write(timestamps.get(i));
                writer.write('\n');

                for (List<String> lines : headersToLines.values()) {
                    writer.write(lines.get(i));
                    writer.write('\n');
                }
            }

            return nmonParser.parse(filename, new StringReader(writer.toString()), timeZone, scaleProcessesByCPU);
        }
        finally {
            if (in != null) {
                in.close();
            }

            if (writer != null) {
                writer.close();
            }
        }

    }
}

@@ -1595,24 +1595,96 @@
 ing%3E%3E(32);%0A%0A
+            List%3CString%3E headers = new java.util.ArrayList%3CString%3E(32);%0A
             
@@ -2038,16 +2038,40 @@
  the top
+ by writing them out now
 %0A       
@@ -2873,32 +2873,109 @@
 uals(header)) %7B%0A
+                            // fix capitalization of LPAR data to match NMON%0A
                 
@@ -3164,36 +3164,35 @@
             
-writer.write
+headers.add
 (line);%0A    
@@ -3211,36 +3211,10 @@
     
-    writer.write('%5Cn');%0A    
+%7D%0A
     
@@ -3238,37 +3238,89 @@
         
+ %7D%0A%0A
      
-%7D%0A
        
-     %7D%0A
+LOGGER.debug(%22found %7B%7D %22 + %22data types%22, headers.size());
 %0A       
@@ -3464,24 +3464,169 @@
 s.size());%0A%0A
+            for (String header : headers) %7B%0A                writer.write(header);%0A                writer.write('%5Cn');%0A            %7D%0A            %0A
             

Fix for topasout parser with non-sorted files