// processFile processes a single bugreport file, and returns the parsing result as a string.
// Writes csv data to csvWriter if a csv file is specified.
func processFile(filePath string, csvWriter *bufio.Writer, isFirstFile bool) string {
	// Read the whole file
	c, err := ioutil.ReadFile(filePath)
	if err != nil {
		log.Fatal(err)
	}
	br, fname, err := bugreportutils.ExtractBugReport(filePath, c)
	if err != nil {
		log.Fatalf("Error getting file contents: %v", err)
	}
	fmt.Printf("Parsing %s\n", fname)

	writer := ioutil.Discard
	if csvWriter != nil && *summaryFormat == parseutils.FormatTotalTime {
		writer = csvWriter
	}

	pkgs, errs := packageutils.ExtractAppsFromBugReport(br)
	if len(errs) > 0 {
		log.Printf("Errors encountered when getting package list: %v\n", errs)
	}
	upm, errs := parseutils.UIDAndPackageNameMapping(br, pkgs)
	if len(errs) > 0 {
		log.Printf("Errors encountered when generating package mapping: %v\n", errs)
	}
	rep := parseutils.AnalyzeHistory(writer, br, *summaryFormat, upm, *scrubPII)

	// Exclude summaries with no change in battery level
	var a []parseutils.ActivitySummary
	for _, s := range rep.Summaries {
		if s.InitialBatteryLevel != s.FinalBatteryLevel {
			a = append(a, s)
		}
	}

	if rep.TimestampsAltered {
		fmt.Println("Some timestamps were changed while processing the log.")
	}
	if len(rep.Errs) > 0 {
		fmt.Println("Errors encountered:")
		for _, err := range rep.Errs {
			fmt.Println(err.Error())
		}
	}
	fmt.Println("\nNumber of summaries ", len(a), "\n")
	for _, s := range a {
		s.Print(&rep.OutputBuffer)
	}

	// Write the battery level summary csv to the csvFile specified
	if csvWriter != nil && *summaryFormat == parseutils.FormatBatteryLevel {
		// The dimension header line is only written if the file is the first one in the directory.
		parseutils.BatteryLevelSummariesToCSV(csvWriter, &a, isFirstFile)
	}

	return rep.OutputBuffer.String()
}
func main() {
	flag.Parse()

	c, err := ioutil.ReadFile(*inputFile)
	if err != nil {
		log.Fatalf("Cannot open the file %s: %v", *inputFile, err)
	}

	br, fname, err := bugreportutils.ExtractBugReport(*inputFile, c)
	if err != nil {
		log.Fatalf("Error getting file contents: %v", err)
	}
	fmt.Printf("Parsing %s\n", fname)
	bs := bugreportutils.ExtractBatterystatsCheckin(br)
	if strings.Contains(bs, "Exception occurred while dumping") {
		log.Fatalf("Exception found in battery dump.")
	}
	m, err := bugreportutils.ParseMetaInfo(br)
	if err != nil {
		log.Fatalf("Unable to get meta info: %v", err)
	}
	s := &sessionpb.Checkin{
		Checkin:          proto.String(bs),
		BuildFingerprint: proto.String(m.BuildFingerprint),
	}
	pkgs, errs := packageutils.ExtractAppsFromBugReport(br)
	if len(errs) > 0 {
		log.Fatalf("Errors encountered when getting package list: %v", errs)
	}

	var ctr checkinutil.IntCounter
	stats, warns, errs := checkinparse.ParseBatteryStats(&ctr, checkinparse.CreateCheckinReport(s), pkgs)
	if len(warns) > 0 {
		log.Printf("Encountered unexpected warnings: %v\n", warns)
	}
	if len(errs) > 0 {
		log.Fatalf("Could not parse battery stats: %v\n", errs)
	}
	fmt.Println("\n################\n")
	fmt.Println("Partial Wakelocks")
	fmt.Println("################\n")
	var pwl []*checkinparse.WakelockInfo
	for _, app := range stats.App {
		for _, pw := range app.Wakelock {
			if pw.GetPartialTimeMsec() > 0 {
				pwl = append(pwl,
					&checkinparse.WakelockInfo{
						Name:     fmt.Sprintf("%s : %s", app.GetName(), pw.GetName()),
						UID:      app.GetUid(),
						Duration: time.Duration(pw.GetPartialTimeMsec()) * time.Millisecond,
					})
			}
		}

	}
	checkinparse.SortByTime(pwl)
	for _, pw := range pwl[:min(5, len(pwl))] {
		fmt.Printf("%s (uid=%d) %s\n", pw.Duration, pw.UID, pw.Name)
	}

	fmt.Println("\n################")
	fmt.Println("Kernel Wakelocks")
	fmt.Println("################\n")
	var kwl []*checkinparse.WakelockInfo
	for _, kw := range stats.System.KernelWakelock {
		if kw.GetName() != "PowerManagerService.WakeLocks" && kw.GetTimeMsec() > 0 {
			kwl = append(kwl, &checkinparse.WakelockInfo{
				Name:     kw.GetName(),
				Duration: time.Duration(kw.GetTimeMsec()) * time.Millisecond,
			})
		}
	}
	checkinparse.SortByTime(kwl)
	for _, kw := range kwl[:min(5, len(kwl))] {
		fmt.Printf("%s %s\n", kw.Duration, kw.Name)
	}

	data, err := proto.Marshal(stats)
	if err != nil {
		log.Fatalf("Error from proto.Marshal: %v", err)
	}
	ioutil.WriteFile("checkin.proto", data, 0600)
}
func main() {
	flag.Parse()

	inputs := strings.Split(*inputFiles, ",")
	if len(inputs) != 2 {
		log.Fatal("wrong input file number, expect 2, got ", len(inputs))
	}

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}
	stats := make([]*bspb.BatteryStats, 2)
	normalizedStats := make([]*bspb.BatteryStats, 2)

	var errs []error
	var warns []string
	var ctr checkinutil.IntCounter
	for i, f := range inputs {
		c, err := ioutil.ReadFile(f)
		if err != nil {
			log.Fatalf("Cannot open the file %s: %v", f, err)
		}
		br, fname, err := bugreportutils.ExtractBugReport(f, c)
		if err != nil {
			log.Fatalf("Error getting file contents: %v", err)
		}
		fmt.Printf("** File #%d: %s\n", i, fname)
		bs := bugreportutils.ExtractBatterystatsCheckin(br)
		if strings.Contains(bs, "Exception occurred while dumping") {
			log.Fatalf("Exception found in battery dump.")
		}
		s := &sessionpb.Checkin{Checkin: proto.String(bs)}

		stats[i], warns, errs = checkinparse.ParseBatteryStats(&ctr, checkinparse.CreateCheckinReport(s), nil)

		if len(errs) > 0 {
			log.Fatalf("Could not parse battery stats: %v", errs)
		}
		if len(warns) > 0 {
			fmt.Printf("Encountered unexpected warnings: %v\n", warns)
		}

		data, err := proto.Marshal(stats[i])
		if err != nil {
			log.Fatalf("Cannot marshal input proto: %v", err)
		}

		if *doDelta {
			ioutil.WriteFile(*parseFileName+strconv.Itoa(i)+".rawproto", data, 0600)
			ioutil.WriteFile(dir+"/"+*parseFileName+strconv.Itoa(i)+".rawproto", data, 0600)
		}

		if *doComp {
			n, err := checkindelta.NormalizeStats(stats[i])
			if err != nil {
				log.Fatalf("Failed to normalize: %v", err)
			}
			normalizedStats[i] = n

			normData, err := proto.Marshal(normalizedStats[i])
			if err != nil {
				log.Fatalf("Cannot marshal normalized input proto: %v", err)
			}
			ioutil.WriteFile(*normalizedFileName+strconv.Itoa(i)+".rawproto", normData, 0600)
			ioutil.WriteFile(dir+"/"+*normalizedFileName+strconv.Itoa(i)+".rawproto", normData, 0600)
		}
	}

	if *doComp {
		fmt.Printf("\n\nNormalized Delta Report (File1 - File2): \n\n")
		for i, f := range inputs {
			fmt.Printf("File %d: %v\n", i+1, f)
		}

		outputProto := checkindelta.ComputeDelta(normalizedStats[0], normalizedStats[1])
		if outputProto == nil {
			log.Fatalf("empty result")
		}
		printResults(outputProto, *compareFileName, dir)
	}
	if *doDelta {
		fmt.Printf("\n\nDelta Report(File1 - File2)- \n\n")
		for i, f := range inputs {
			fmt.Printf("File %d: %v\n", i+1, f)
		}
		outputProto := checkindelta.ComputeDelta(stats[0], stats[1])
		if outputProto == nil {
			log.Fatalf("empty result")
		}
		printResults(outputProto, *deltaFileName, dir)
	}
}