"use strict";

function FlightLogIndex(logData) {
    //Private:
    var 
        that = this,
        logBeginOffsets = false,
        logCount = false,
        intraframeDirectories = false;
        
    function buildLogOffsetsIndex() {
        var 
            stream = new ArrayDataStream(logData), 
            i, logStart;
        
        logBeginOffsets = [];
    
        for (i = 0; ; i++) {
            logStart = stream.nextOffsetOf(FlightLogParser.prototype.FLIGHT_LOG_START_MARKER);
    
            if (logStart == -1) {
                //No more logs found in the file
                logBeginOffsets.push(stream.end);
                break; 
            }
    
            logBeginOffsets.push(logStart);
            
            //Restart the search after this header
            stream.pos = logStart + FlightLogParser.prototype.FLIGHT_LOG_START_MARKER.length;
        }
    }
    
    function buildIntraframeDirectories() {
        var 
            parser = new FlightLogParser(logData, that);
        
        intraframeDirectories = [];

        for (var i = 0; i < that.getLogCount(); i++) {
            var 
                intraIndex = {
                    times: [],
                    offsets: [],
                    avgThrottle: [],
                    initialSlow: [],
                    initialGPSHome: [],
                    hasEvent: [],
                    minTime: false,
                    maxTime: false
                },
                
                gyroADC, accSmooth, magADC,
                
                iframeCount = 0,
                motorFields = [],
                matches,
                throttleTotal,
                eventInThisChunk = null,
                parsedHeader,
                sawEndMarker = false;
            
            try {
                parser.parseHeader(logBeginOffsets[i], logBeginOffsets[i + 1]);
                parsedHeader = true;
            } catch (e) {
                console.log("Error parsing header of log #" + (i + 1) + ": " + e);
                intraIndex.error = e;
                
                parsedHeader = false;
            }

            // Only attempt to parse the log if the header wasn't corrupt
            if (parsedHeader) {
                var 
                    sysConfig = parser.sysConfig,
                    mainFrameDef = parser.frameDefs.I,
                    
                    gyroADC = [mainFrameDef.nameToIndex["gyroADC[0]"], mainFrameDef.nameToIndex["gyroADC[1]"], mainFrameDef.nameToIndex["gyroADC[2]"]],
                    accSmooth = [mainFrameDef.nameToIndex["accSmooth[0]"], mainFrameDef.nameToIndex["accSmooth[1]"], mainFrameDef.nameToIndex["accSmooth[2]"]],
                    magADC = [mainFrameDef.nameToIndex["magADC[0]"], mainFrameDef.nameToIndex["magADC[1]"], mainFrameDef.nameToIndex["magADC[2]"]],
                    
                    lastSlow = [],
                    lastGPSHome = [];
                
                // Identify motor fields so they can be used to show the activity summary bar
                for (var j = 0; j < 8; j++) {
                    if (mainFrameDef.nameToIndex["motor[" + j + "]"] !== undefined) {
                        motorFields.push(mainFrameDef.nameToIndex["motor[" + j + "]"]);
                    }
                }
                
                // Do we have mag fields? If not mark that data as absent
                if (magADC[0] === undefined) {
                    magADC = false;
                }
                
                parser.onFrameReady = function(frameValid, frame, frameType, frameOffset, frameSize) {
                    if (!frameValid) {
                        return;
                    }
                    
                    switch (frameType) {
                        case 'P':
                        case 'I':
                            var 
                                frameTime = frame[FlightLogParser.prototype.FLIGHT_LOG_FIELD_INDEX_TIME];
                            
                            if (intraIndex.minTime === false) {
                                intraIndex.minTime = frameTime;
                            }
                            
                            if (intraIndex.maxTime === false || frameTime > intraIndex.maxTime) {
                                intraIndex.maxTime = frameTime;
                            }
                            
                            if (frameType == 'I') {
                                // Start a new chunk on every 4th I-frame
                                if (iframeCount % 4 === 0) {
                                    // Log the beginning of the new chunk
                                    intraIndex.times.push(frameTime);
                                    intraIndex.offsets.push(frameOffset);
                                    
                                    if (motorFields.length) {
                                        throttleTotal = 0;
                                        for (var j = 0; j < motorFields.length; j++) {
                                            throttleTotal += frame[motorFields[j]];
                                        }
                                        
                                        intraIndex.avgThrottle.push(Math.round(throttleTotal / motorFields.length));
                                    }
                                    
                                    /* To enable seeking to an arbitrary point in the log without re-reading anything
                                     * that came before, we have to record the initial state of various items which aren't
                                     * logged anew every iteration.
                                     */ 
                                    intraIndex.initialSlow.push(lastSlow);
                                    intraIndex.initialGPSHome.push(lastGPSHome);
                                }
                                
                                iframeCount++;
                            }
                        break;
                        case 'H':
                            lastGPSHome = frame.slice(0);
                        break;
                        case 'E':
                            // Mark that there was an event inside the current chunk
                            if (intraIndex.times.length > 0) {
                                intraIndex.hasEvent[intraIndex.times.length - 1] = true;
                            }
                            
                            if (frame.event == FlightLogEvent.LOG_END) {
                                sawEndMarker = true;
                            }
                        break;
                        case 'S':
                            lastSlow = frame.slice(0);
                        break;
                    }
                };
            
                try {
                    parser.parseLogData(false);
                } catch (e) {
                    intraIndex.error = e;
                }
                
                // Don't bother including the initial (empty) states for S and H frames if we didn't have any in the source data
                if (!parser.frameDefs.S) {
                    delete intraIndex.initialSlow;
                }

                if (!parser.frameDefs.H) {
                    delete intraIndex.initialGPSHome;
                }

                intraIndex.stats = parser.stats;
            }
            
            // Did we not find any events in this log?
            if (intraIndex.minTime === false) {
                if (sawEndMarker) {
                    intraIndex.error = "Logging paused, no data";
                } else {
                    intraIndex.error = "Log truncated, no data";
                }
            }
        
            intraframeDirectories.push(intraIndex);
        }
    }
    
    //Public: 
    this.loadFromJSON = function(json) {
        
    };
    
    this.saveToJSON = function() {
        var 
            intraframeDirectories = this.getIntraframeDirectories(),
            i, j, 
            resultIndexes = new Array(intraframeDirectories.length);
        
        for (i = 0; i < intraframeDirectories.length; i++) {
            var 
                lastTime, lastLastTime, 
                lastOffset, lastLastOffset,
                lastThrottle,
                
                sourceIndex = intraframeDirectories[i],
                
                resultIndex = {
                    times: new Array(sourceIndex.times.length), 
                    offsets: new Array(sourceIndex.offsets.length),
                    minTime: sourceIndex.minTime,
                    maxTime: sourceIndex.maxTime,
                    avgThrottle: new Array(sourceIndex.avgThrottle.length)
                };
            
            if (sourceIndex.times.length > 0) {
                resultIndex.times[0] = sourceIndex.times[0];
                resultIndex.offsets[0] = sourceIndex.offsets[0];
                
                lastLastTime = lastTime = sourceIndex.times[0];
                lastLastOffset = lastOffset = sourceIndex.offsets[0];
                
                for (j = 1; j < sourceIndex.times.length; j++) {
                    resultIndex.times[j] = sourceIndex.times[j] - 2 * lastTime + lastLastTime;
                    resultIndex.offsets[j] = sourceIndex.offsets[j] - 2 * lastOffset + lastLastOffset;
                    
                    lastLastTime = lastTime;
                    lastTime = sourceIndex.times[j];
    
                    lastLastOffset = lastOffset;
                    lastOffset = sourceIndex.offsets[j];
                }
            }
            
            if (sourceIndex.avgThrottle.length > 0) {
                for (j = 0; j < sourceIndex.avgThrottle.length; j++) {
                    resultIndex.avgThrottle[j] = sourceIndex.avgThrottle[j] - 1000;
                }
            }
            
            resultIndexes[i] = resultIndex;
        }
        
        return JSON.stringify(resultIndexes);
    };  
    
    this.getLogBeginOffset = function(index) {
        if (!logBeginOffsets)
            buildLogOffsetsIndex();
        
        return logBeginOffsets[index];
    };
    
    this.getLogCount = function() {
        if (!logBeginOffsets)
            buildLogOffsetsIndex();

        return logBeginOffsets.length - 1;
    };
    
    this.getIntraframeDirectories = function() {
        if (!intraframeDirectories)
            buildIntraframeDirectories();
        
        return intraframeDirectories;
    };
    
    this.getIntraframeDirectory = function(logIndex) {
        return this.getIntraframeDirectories()[logIndex];
    };
}
