// Copyright 2021, Chef.  All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)

package main

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"

	"github.com/q191201771/naza/pkg/nazalog"
)

// 分析c++程序pstack的两次结果的差异

type PstackInfo struct {
	tis []ThreadInfo
	tim map[string]ThreadInfo
}

type ThreadInfo struct {
	Num int
	P   string
	Id  int

	RawLine       string
	RawStackLines string
}

func NewPstackInfo(filename string) PstackInfo {
	var tis []ThreadInfo

	contents, err := ioutil.ReadFile(filename)
	nazalog.Assert(nil, err)
	content := string(contents)
	lines := strings.Split(content, "\n")
	//nazalog.Debugf("len(lines)=%d", len(lines))

	var ti *ThreadInfo
	for _, line := range lines {
		if strings.HasPrefix(line, "Thread") {
			if ti != nil {
				tis = append(tis, *ti)
			}
			ti = &ThreadInfo{}

			//nazalog.Debugf("%s", line)
			ti.RawLine = line
			ti.Num, ti.P, ti.Id, err = parseThreadLine(line)
			nazalog.Assert(nil, err)
			continue
		}

		ti.RawStackLines += line + "\n"
	}
	if ti != nil {
		tis = append(tis, *ti)
	}
	nazalog.Debugf("len(tis)=%d", len(tis))

	tim := make(map[string]ThreadInfo)
	for _, ti := range tis {
		tim[ti.Uk()] = ti
	}

	return PstackInfo{
		tis: tis,
		tim: tim,
	}
}

func (pi *PstackInfo) Find(uk string) (ThreadInfo, bool) {
	ti, exist := pi.tim[uk]
	return ti, exist
}

func (ti *ThreadInfo) Uk() string {
	return fmt.Sprintf("%s_%d", ti.P, ti.Id)
}

func parseThreadLine(line string) (num int, p string, id int, err error) {
	p1 := strings.Index(line, "Thread")
	p2 := strings.Index(line, "(Thread")
	num, err = strconv.Atoi(line[p1+7 : p2-1])
	if err != nil {
		return
	}

	p3 := strings.Index(line, "(LWP")
	p = line[p2+8 : p3-1]

	p4 := strings.Index(line, "))")
	id, err = strconv.Atoi(line[p3+5 : p4-1])

	return
}

func main() {
	_ = nazalog.Init(func(option *nazalog.Option) {
		option.LevelFlag = false
		option.ShortFileFlag = false
		option.TimestampFlag = false
	})

	pi1 := NewPstackInfo("old.txt")
	pi2 := NewPstackInfo("new.txt")
	for _, ti2 := range pi2.tis {
		var pre, suf string
		suf = "\033[0m"
		ti1, exist := pi1.Find(ti2.Uk())
		if exist {
			if ti2.RawStackLines == ti1.RawStackLines {
				// 1, 2都有,但是堆栈没变 没色
				pre = ""
				suf = ""
				nazalog.Debugf("%s-------------------------------------------------------------------------%s", pre, suf)
				nazalog.Debugf("%s%s%s", pre, ti2.RawLine, suf)
				nazalog.Debugf("%s%s%s", pre, ti2.RawStackLines, suf)
			} else {
				// 1, 2都有,但是堆栈变化 红色
				// 注意,断站有变化也可能是函数参数变化了
				pre = "\033[22;31m"
				nazalog.Debugf("%s-------------------------------------------------------------------------%s", pre, suf)
				nazalog.Debugf("%s%s%s", pre, ti2.RawLine, suf)
				nazalog.Debugf("%s%s%s", pre, ti2.RawStackLines, suf)
			}
		} else {
			// 只在2有 绿色
			pre = "\033[22;36m"
			nazalog.Debugf("%s-------------------------------------------------------------------------%s", pre, suf)
			nazalog.Debugf("%s%s%s", pre, ti2.RawLine, suf)
			nazalog.Debugf("%s%s%s", pre, ti2.RawStackLines, suf)
		}
	}
}