// AnalyzeFiles processes and analyzes a bugreport package.
func (pd *ParsedData) AnalyzeFiles(files []UploadedFile, refCount int) error {
	hasBugreport := false
	for _, file := range files {
		contents := string(file.Contents)
		switch file.FileType {
		case "bugreport", "bugreport2":
			// Parse the bugreport.
			if err := pd.parseBugReport(file.FileName, contents); err != nil {
				return fmt.Errorf("error parsing bugreport: %v", err)
			}
			if file.FileType == "bugreport" {
				hasBugreport = true
			}
			// Write the bug report to a file in case we need it to process a kernel trace file.
			if len(pd.data) < numberOfFilesToCompare {
				tmpFile, err := writeTempFile(contents)
				if err != nil {
					return fmt.Errorf("could not write bugreport: %v", err)
				}
				pd.bugReport = tmpFile
			}

		case "kernel":
			if !kernel.IsTrace(file.Contents) {
				return fmt.Errorf("invalid kernel trace file: %v", file.FileName)
			}
			if pd.kernelTrace != "" {
				log.Printf("more than one kernel trace file found")
				continue
			}
			// Need bug report to process kernel trace file, so store the file for later processing.
			tmpFile, err := writeTempFile(contents)
			if err != nil {
				return fmt.Errorf("could not write kernel trace file: %v", err)
			}
			pd.kernelTrace = tmpFile

		case "powermonitor":
			// Parse the powermonitor file.
			if err := pd.parsePowermonitorFile(file.FileName, contents); err != nil {
				return fmt.Errorf("error parsing powermonitor file: %v", err)
			}
		default:
			// File does not have a supported file type.
			return fmt.Errorf("invalid file %s of type %s", file.FileName, file.FileType)
		}
	}

	if !hasBugreport {
		return errors.New("missing bugreport file")
	}
	return nil
}
// HTTPAnalyzeHandler processes the bugreport package uploaded via an http request's multipart body.
func HTTPAnalyzeHandler(w http.ResponseWriter, r *http.Request) {
	// Do not accept files that are greater than 50 MBs
	if r.ContentLength > maxFileSize {
		closeConnection(w, "File too large (>50MB).")
		return
	}
	r.Body = http.MaxBytesReader(w, r.Body, maxFileSize)
	log.Printf("Trace starting reading uploaded file. %d bytes", r.ContentLength)
	defer log.Printf("Trace ended analyzing file.")

	var refCount int
	//get the multipart reader for the request.
	reader, err := r.MultipartReader()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	var fs []UploadedFile
	//copy each part to destination.
	for {
		part, err := reader.NextPart()
		if err == io.EOF {
			break
		}

		// If part.FileName() is empty, skip this iteration.
		if part.FileName() == "" {
			continue
		}

		b, err := ioutil.ReadAll(part)
		if err != nil {
			http.Error(w, "Failed to read file. Please try again.", http.StatusInternalServerError)
			return
		}
		if len(b) == 0 {
			continue
		}

		files, err := bugreportutils.Contents(part.FileName(), b)
		if err != nil {
			http.Error(w, fmt.Sprintf("failed to read file contents: %v", err), http.StatusInternalServerError)
			return
		}

		var contents []byte
		valid := false
		fname := ""
	contentLoop:
		for n, f := range files {
			switch part.FormName() {
			case "bugreport", "bugreport2":
				if bugreportutils.IsBugReport(f) {
					// TODO: handle the case of additional kernel and powermonitor files within a single uploaded file
					valid = true
					contents = f
					fname = n
					break contentLoop
				}
			case "kernel":
				if kernel.IsTrace(f) {
					valid = true
					contents = f
					fname = n
					break contentLoop
				}
			case "powermonitor":
				if powermonitor.IsValid(f) {
					valid = true
					contents = f
					fname = n
					break contentLoop
				}
			default:
				valid = true
				contents = f
				fname = n
				break contentLoop
			}
		}

		if !valid {
			http.Error(w, fmt.Sprintf("%s does not contain a valid %s file", part.FileName(), part.FormName()), http.StatusInternalServerError)
			return
		}

		fs = append(fs, UploadedFile{part.FormName(), fname, contents})
	}
	AnalyzeAndResponse(w, fs, refCount)
}