package parser

import (
	"regexp"
	"strconv"
	"strings"
)

var parseFuncList = []func(chan string, chan jsonObject){
	parseMemory,
	parseMemoryLayout,
	parseTimestamps,
	parseStatus,
	parseHashTables,
	parseLinux,
	parseCpu,
	parseModel,
	parseDeviceFound,
	parseKernelCommandLine,
	parseCma,
	parseSlub,
	parsePidMax,
	parseWorkingset,
	parseBootloaderDram,
	parseBootloaderMmc,
	parseBootloaderConsole,
	parseBootloaderGadgetDriver,
	parseBootloaderSetupSize,
	parseBootloaderCpu,
	parseBootloaderBoard,
	parseBootloaderRstStat,
	parseBootloaderNet,
	parseBootloaderBootcode,
	parseBootloaderRead,
	parseBootloaderCommandLine,
	parseBootloaderLoad,
}

var reTimestamp = regexp.MustCompile(`^\[[[:blank:]]*[[:digit:]]+\.[[:digit:]]+[[:blank:]]*\]`)
var reAddress = regexp.MustCompile(`0x[[:xdigit:]]+`)

func parseMemory(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "memory"

	rLine := regexp.MustCompile("Memory: [[:ascii:]]+ available")
	rBytes := regexp.MustCompile("[[:digit:]]+[Kk]")
	for line := range lines {
		if found := rLine.FindString(line); found != "" {
			r := rBytes.FindAllString(line, 2)
			m["available"] = r[0]
			m["total"] = r[1]
			chanOutput <- m
			return
		}
	}

	m["available"] = ""
	m["total"] = ""

	chanOutput <- m
}

func parseMemoryLayout(lines chan string, chanOutput chan jsonObject) {
	rHeaderArm := regexp.MustCompile("Virtual kernel memory layout:")
	rHeaderX86 := regexp.MustCompile("Zone ranges:")
	for line := range lines {
		if rHeaderArm.MatchString(line) {
			parseMemoryLayoutArm(lines, chanOutput)
			return
		}

		if rHeaderX86.MatchString(line) {
			parseMemoryLayoutX86(lines, chanOutput)
			return
		}
	}

	m := make(jsonObject)
	m["type"] = "memory_layout"
	m["layout"] = make([]jsonObject, 0)

	chanOutput <- m
}

func parseMemoryLayoutArm(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "memory_layout"
	m["layout"] = make([]jsonObject, 0)

	rLine := regexp.MustCompile(`[[:print:]]+:[[:blank:]]*0x[[:xdigit:]]+[[:blank:]]*-[[:blank:]]*0x[[:xdigit:]]+`)
	rLabel := regexp.MustCompile(`[[:graph:]]+`)

	for line := range lines {
		line = removeTimestamp(line)
		if rLine.MatchString(line) {
			item := make(jsonObject)
			item["label"] = rLabel.FindString(line)

			r := reAddress.FindAllString(line, 2)
			item["from"] = r[0]
			item["to"] = r[1]

			m["layout"] = append(m["layout"].([]jsonObject), item)
		} else {
			break
		}
	}

	chanOutput <- m
}

func parseMemoryLayoutX86(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "memory_layout"
	m["layout"] = make([]jsonObject, 0)

	rLine := regexp.MustCompile(`[[:print:]]+\[mem[[:blank:]]+0x[[:xdigit:]]+-0x[[:xdigit:]]+\]`)
	rLabel := regexp.MustCompile(`[[:graph:]]+`)

	for line := range lines {
		line = removeTimestamp(line)
		if rLine.MatchString(line) {
			item := make(jsonObject)
			item["label"] = rLabel.FindString(line)

			r := reAddress.FindAllString(line, 2)
			item["from"] = r[0]
			item["to"] = r[1]

			m["layout"] = append(m["layout"].([]jsonObject), item)
		} else {
			break
		}
	}

	chanOutput <- m
}

func parseTimestamps(lines chan string, chanOutput chan jsonObject) {
	rVal := regexp.MustCompile(`[[:digit:]]+\.[[:digit:]]+`)

	list := make(jsonArray, 0)
	for line := range lines {
		if reTimestamp.MatchString(line) {
			t := reTimestamp.FindString(line)
			v := rVal.FindString(t)
			f, _ := strconv.ParseFloat(v, 64)
			list = append(list, f)
		}
	}

	m := make(jsonObject)
	m["type"] = "timestamps"
	m["timestamps"] = list

	chanOutput <- m
}

func parseStatus(lines chan string, chanOutput chan jsonObject) {
	rLine := regexp.MustCompile(`^\[[[:blank:]]*(OK|FAIL)[[:blank:]]*\][[:blank:]]+`)
	rOk := regexp.MustCompile(`^\[[[:blank:]]*OK[[:blank:]]*\]`)

	list := make([]jsonObject, 0)
	for line := range lines {
		if rLine.MatchString(line) {
			item := make(jsonObject)
			if rOk.MatchString(line) {
				item["status"] = "ok"
			} else {
				item["status"] = "fail"
			}

			prefix := rLine.FindString(line)
			label := strings.TrimPrefix(line, prefix)
			item["label"] = label

			list = append(list, item)
		}
	}

	m := make(jsonObject)
	m["type"] = "status"
	m["list"] = list

	chanOutput <- m
}

func parseHashTables(lines chan string, chanOutput chan jsonObject) {
	rLine := regexp.MustCompile(`[[:graph:]]+ hash table entries:`)

	list := make([]jsonObject, 0)
	for line := range lines {
		if rLine.MatchString(line) {
			line = removeTimestamp(line)

			item := make(jsonObject)

			label := regexp.MustCompile(`[[:print:]]+ hash table entries:`).FindString(line)

			item["label"] = strings.TrimSuffix(label, " hash table entries:")

			entries := regexp.MustCompile(`hash table entries: [[:digit:]]+`).FindString(line)
			entries = strings.TrimPrefix(entries, "hash table entries: ")

			item["entries"], _ = strconv.ParseInt(entries, 10, 64)

			order := regexp.MustCompile(`order:? [[:digit:]-]+`).FindString(line)
			orderPrefix := regexp.MustCompile(`order:? `).FindString(order)
			order = strings.TrimPrefix(order, orderPrefix)

			item["order"], _ = strconv.ParseInt(order, 10, 64)

			size := regexp.MustCompile(`[[:digit:]]+ bytes`).FindString(line)
			size = strings.TrimSuffix(size, " bytes")

			item["size"], _ = strconv.ParseInt(size, 10, 64)

			list = append(list, item)
		}
	}

	m := make(jsonObject)
	m["type"] = "hash_tables"
	m["list"] = list

	chanOutput <- m
}

func parseLinux(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "linux"
	m["version"] = ""
	m["builder"] = ""
	m["toolchain"] = ""
	m["label"] = ""

	rLine := regexp.MustCompile(`Linux version[[:blank:]]+[[:graph:]]+[[:blank:]]+([[:print:]]+)[[:blank:]]+([[:print:]]+)[[:blank:]]+#[[:print:]]+`)
	for line := range lines {
		if rLine.MatchString(line) {
			line = rLine.FindString(line)
			line = strings.TrimPrefix(line, "Linux version ")

			m["version"] = regexp.MustCompile(`[[:graph:]]+`).FindString(line)
			m["os"] = "linux"

			bracketed := regexp.MustCompile(`\([[:print:]]+?\)`).FindAllString(line, 2)
			for i, s := range bracketed {
				switch i {
				case 0:
					m["builder"] = removeBrackets(s)
				case 1:
					m["toolchain"] = removeBrackets(s)
				}
			}

			m["label"] = regexp.MustCompile(`#[[:print:]]+`).FindString(line)

			break
		}
	}

	chanOutput <- m
}

func parseCpu(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "cpu"
	m["name"] = ""
	m["id"] = ""
	m["revision"] = ""
	m["arch"] = ""
	m["cr"] = ""
	m["data_cache"] = ""
	m["instruction_cache"] = ""

	rLine1 := regexp.MustCompile(`CPU:[[:blank:]]+[[:print:]]+[[:blank:]]+\[[[:xdigit:]]+\][[:blank:]]+revision[[:blank:]]+[[:digit:]][[:blank:]]+\([[:print:]]+\), cr=[[:xdigit:]]+`)
	rLine2 := regexp.MustCompile(`CPU:[[:blank:]]+[[:print:]]+[[:blank:]]+data cache,[[:blank:]]+[[:print:]]+[[:blank:]]+instruction cache`)
	for line := range lines {
		if rLine1.MatchString(line) {
			line = rLine1.FindString(line)
			line = strings.TrimPrefix(line, "CPU: ")

			name := regexp.MustCompile(`[[:print:]]+\[`).FindString(line)
			name = strings.TrimSuffix(name, "[")
			name = strings.TrimSpace(name)

			m["name"] = name

			id := regexp.MustCompile(`\[[[:xdigit:]]+\]`).FindString(line)
			id = strings.TrimPrefix(id, "[")
			id = strings.TrimSuffix(id, "]")

			m["id"] = id

			revision := regexp.MustCompile(`revision[[:blank:]]+[[:digit:]]+`).FindString(line)
			revision = strings.TrimPrefix(revision, "revision")
			revision = strings.TrimSpace(revision)

			m["revision"] = revision

			arch := regexp.MustCompile(`\([[:print:]]+\)`).FindString(line)
			arch = strings.TrimPrefix(arch, "(")
			arch = strings.TrimSuffix(arch, ")")
			arch = strings.TrimSpace(arch)

			m["arch"] = arch

			cr := regexp.MustCompile(`cr=[[:xdigit:]]+`).FindString(line)
			cr = strings.TrimPrefix(cr, "cr=")

			m["cr"] = cr
		} else if rLine2.MatchString(line) {
			line = rLine2.FindString(line)
			line = strings.TrimPrefix(line, `CPU: `)

			dataCache := regexp.MustCompile(`[[:print:]]+data cache`).FindString(line)
			dataCache = strings.TrimSuffix(dataCache, "data cache")
			dataCache = strings.TrimSpace(dataCache)

			m["data_cache"] = dataCache

			instructionCache := regexp.MustCompile(`,[[:print:]]+instruction cache`).FindString(line)
			instructionCache = strings.TrimPrefix(instructionCache, ",")
			instructionCache = strings.TrimSuffix(instructionCache, "instruction cache")
			instructionCache = strings.TrimSpace(instructionCache)

			m["instruction_cache"] = instructionCache
		}
	}

	chanOutput <- m
}

func parseModel(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "model"
	m["model"] = ""

	rLine := regexp.MustCompile(`(Machine model: [[:print:]]+)|(Machine: [[:print:]]+)`)
	rLineEdison := regexp.MustCompile(`Linux version[[:print:]]+edison`)
	rLineARTIK := regexp.MustCompile(`CPU EXYNOS3250`)
	for line := range lines {
		if rLine.MatchString(line) {
			line = rLine.FindString(line)
			line = strings.TrimPrefix(line, "Machine model: ")
			line = strings.TrimPrefix(line, "Machine: ")

			m["model"] = strings.TrimSpace(line)

			break
		} else if rLineEdison.MatchString(line) {
			m["model"] = "Intel Edison"

			break
		} else if rLineARTIK.MatchString(line) {
			m["model"] = "SAMSUNG ARTIK5"

			break
		}
	}

	chanOutput <- m
}

func parseDeviceFound(lines chan string, chanOutput chan jsonObject) {
	list := make([]string, 0)
	for line := range lines {
		for _, e := range deviceTable {
			if e.re.MatchString(line) {
				list = append(list, e.name)
			}
		}
	}

	m := make(jsonObject)
	m["type"] = "device_found"
	m["list"] = list

	chanOutput <- m
}

func parseKernelCommandLine(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "kernel_command_line"
	m["kernel_command_line"] = ""

	rLine := regexp.MustCompile("Kernel command line: [[:print:]]+")
	for line := range lines {
		if rLine.MatchString(line) {
			line = rLine.FindString(line)

			cmdline := strings.TrimPrefix(line, "Kernel command line: ")
			cmdline = strings.TrimSpace(cmdline)
			if strings.HasPrefix(cmdline, `"`) && strings.HasSuffix(cmdline, `"`) {
				cmdline = strings.TrimPrefix(cmdline, `"`)
				cmdline = strings.TrimSuffix(cmdline, `"`)
			}

			m["kernel_command_line"] = cmdline

			break
		}
	}

	chanOutput <- m
}

func parseCma(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "cma"
	m["size"] = ""
	m["address"] = ""

	rLine := regexp.MustCompile("cma: Reserved [[:print:]]+ at [[:print:]]+")
	for line := range lines {
		if rLine.MatchString(line) {
			line = rLine.FindString(line)
			line = strings.TrimPrefix(line, "cma: Reserved ")

			size := regexp.MustCompile(`[[:print:]]+ at`).FindString(line)
			size = strings.TrimSuffix(size, "at")
			size = strings.TrimSpace(size)

			m["size"] = size

			address := regexp.MustCompile(`at [[:print:]]+`).FindString(line)
			address = strings.TrimPrefix(address, "at")
			address = strings.TrimSpace(address)

			m["address"] = address

			break
		}
	}

	chanOutput <- m
}

func parseSlub(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "slub"
	m["hw_align"] = ""
	m["order"] = ""
	m["min_objects"] = ""
	m["cpus"] = ""
	m["nodes"] = ""

	rLine := regexp.MustCompile("SLUB: HWalign=[[:digit:]]+, Order=[[:digit:]]+-[[:digit:]]+, MinObjects=[[:digit:]]+, CPUs=[[:digit:]]+, Nodes=[[:digit:]]+")
	for line := range lines {
		if rLine.MatchString(line) {
			line = rLine.FindString(line)

			hwAlign := regexp.MustCompile(`HWalign=[[:digit:]]+`).FindString(line)
			hwAlign = strings.TrimPrefix(hwAlign, `HWalign=`)

			m["hw_align"] = hwAlign

			order := regexp.MustCompile(`Order=[[:digit:]]+-[[:digit:]]+`).FindString(line)
			order = strings.TrimPrefix(order, `Order=`)

			m["order"] = order

			minObjects := regexp.MustCompile(`MinObjects=[[:digit:]]+`).FindString(line)
			minObjects = strings.TrimPrefix(minObjects, `MinObjects=`)

			m["min_objects"] = minObjects

			cpus := regexp.MustCompile(`CPUs=[[:digit:]]+`).FindString(line)
			cpus = strings.TrimPrefix(cpus, `CPUs=`)

			m["cpus"] = cpus

			nodes := regexp.MustCompile(`Nodes=[[:digit:]]+`).FindString(line)
			nodes = strings.TrimPrefix(nodes, `Nodes=`)

			m["nodes"] = nodes

			break
		}
	}

	chanOutput <- m
}

func parsePidMax(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "pid_max"
	m["default"] = ""
	m["min"] = ""

	rLine := regexp.MustCompile("pid_max:[[:blank:]]+default:[[:blank:]]*[[:digit:]]+[[:blank:]]+minimum:[[:blank:]]*[[:digit:]]+")
	for line := range lines {
		if rLine.MatchString(line) {
			line = rLine.FindString(line)

			m["default"] = extract(line,
				`default:[[:blank:]]*[[:digit:]]+`,
				`default:[[:blank:]]*`,
				``)

			m["min"] = extract(line,
				`minimum:[[:blank:]]*[[:digit:]]+`,
				`minimum:[[:blank:]]*`,
				``)

			break
		}
	}

	chanOutput <- m
}

func parseWorkingset(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "workingset"
	m["timestamp bits"] = ""
	m["max_order"] = ""
	m["bucket_order"] = ""

	rLine := regexp.MustCompile("workingset: timestamp_bits=[[:digit:]]+ max_order=[[:digit:]]+ bucket_order=[[:digit:]]+")
	for line := range lines {
		if rLine.MatchString(line) {
			line = rLine.FindString(line)

			m["timestamp_bits"] = extract(line,
				`timestamp_bits=[[:digit:]]+`,
				`timestamp_bits=`,
				``)

			m["max_order"] = extract(line,
				`max_order=[[:digit:]]+`,
				`max_order=`,
				``)

			m["bucket_order"] = extract(line,
				`bucket_order=[[:digit:]]+`,
				`bucket_order=`,
				``)

			break
		}
	}

	chanOutput <- m
}

func parseBootloaderDram(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_dram"
	m["value"] = ""

	rLine := regexp.MustCompile(`^DRAM:\s+([[:print:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 1 {
				m["value"] = sub[1]
				break
			}
		}
	}

	chanOutput <- m
}

func parseBootloaderMmc(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_mmc"
	m["value"] = ""

	rLine := regexp.MustCompile(`^MMC:\s+([[:print:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 1 {
				m["value"] = sub[1]
				break
			}
		}
	}

	chanOutput <- m
}

func parseBootloaderConsole(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_console"
	m["in"] = ""
	m["out"] = ""
	m["err"] = ""

	const (
		FIND_IN = iota
		FIND_OUT
		FIND_ERR
		FIN
	)

	state := FIND_IN

	rIn := regexp.MustCompile(`^In:\s+([[:print:]]+)`)
	rOut := regexp.MustCompile(`^Out:\s+([[:print:]]+)`)
	rErr := regexp.MustCompile(`^Err:\s+([[:print:]]+)`)
	for line := range lines {
		switch state {
		case FIND_IN:
			if sub := rIn.FindStringSubmatch(line); len(sub) > 1 {
				m["in"] = sub[1]
				state = FIND_OUT
			}
		case FIND_OUT:
			if sub := rOut.FindStringSubmatch(line); len(sub) > 1 {
				m["out"] = sub[1]
				state = FIND_ERR
			}
		case FIND_ERR:
			if sub := rErr.FindStringSubmatch(line); len(sub) > 1 {
				m["err"] = sub[1]
				state = FIN
			}
		}

		if state == FIN {
			break
		}
	}

	chanOutput <- m
}

func parseBootloaderGadgetDriver(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_gadget_driver"
	m["value"] = ""

	rLine := regexp.MustCompile(`^GADGET DRIVER:\s+([[:print:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 1 {
				m["value"] = sub[1]
				break
			}
		}
	}

	chanOutput <- m
}

func parseBootloaderSetupSize(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_setup_size"
	m["value"] = ""

	rLine := regexp.MustCompile(`^Setup Size =\s+([[:print:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 1 {
				m["value"] = sub[1]
				break
			}
		}
	}

	chanOutput <- m
}

func parseBootloaderCpu(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_cpu"
	m["value"] = ""

	rLine := regexp.MustCompile(`^CPU:\s+([[:print:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 1 {
				m["value"] = sub[1]
				break
			}
		}
	}

	chanOutput <- m
}

func parseBootloaderBoard(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_board"
	m["value"] = ""

	rLine := regexp.MustCompile(`^Board:\s+([[:print:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 1 {
				m["value"] = sub[1]
				break
			}
		}
	}

	chanOutput <- m
}

func parseBootloaderRstStat(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_rst_stat"
	m["value"] = ""

	rLine := regexp.MustCompile(`^rst_stat\s+:\s+([[:print:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 1 {
				m["value"] = sub[1]
				break
			}
		}
	}

	chanOutput <- m
}

func parseBootloaderNet(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_net"
	m["value"] = ""

	rLine := regexp.MustCompile(`^Net:\s+([[:print:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 1 {
				m["value"] = sub[1]
				break
			}
		}
	}

	chanOutput <- m
}

func parseBootloaderBootcode(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_bootcode"
	m["value"] = ""

	rLine := regexp.MustCompile(`^Raspberry Pi Bootcode`)
	for line := range lines {
		if rLine.MatchString(line) {
			m["value"] = line
			break
		}
	}

	chanOutput <- m
}

func parseBootloaderRead(lines chan string, chanOutput chan jsonObject) {
	const (
		FIND_NAME = iota
		FIND_SIZE
	)

	state := FIND_NAME
	elements := make(map[string]string)
	name := ""

	rLine := regexp.MustCompile(`brfs: File read: ([[:print:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 1 {
				switch state {
				case FIND_NAME:
					name = sub[1]
					state = FIND_SIZE
				case FIND_SIZE:
					elements[name] = sub[1]
					state = FIND_NAME
				}
			}
		}
	}

	list := make([]jsonObject, 0)
	for name, size := range elements {
		list = append(list, jsonObject{"name": name, "size": size})
	}

	m := make(jsonObject)
	m["type"] = "bootloader_read"
	m["list"] = list

	chanOutput <- m
}

func parseBootloaderCommandLine(lines chan string, chanOutput chan jsonObject) {
	m := make(jsonObject)
	m["type"] = "bootloader_command_line"
	m["value"] = ""

	const (
		FIND_HEADER = iota
		FIND_LINE
		DONE
	)

	state := FIND_HEADER

	rLine := regexp.MustCompile(`Read command line from file`)
	rValue := regexp.MustCompile(`[[:graph:]]+ ([[:print:]]+)`)
	for line := range lines {
		switch state {
		case FIND_HEADER:
			if rLine.MatchString(line) {
				state = FIND_LINE
			}
		case FIND_LINE:
			if sub := rValue.FindStringSubmatch(line); len(sub) > 1 {
				m["value"] = sub[1]
				state = DONE
			}
		}

		if state == DONE {
			break
		}
	}

	chanOutput <- m
}

func parseBootloaderLoad(lines chan string, chanOutput chan jsonObject) {
	list := make([]jsonObject, 0)

	rLine := regexp.MustCompile(`Loading '([[:print:]]+)' to (0x[[:xdigit:]]+) size (0x[[:xdigit:]]+)`)
	for line := range lines {
		if rLine.MatchString(line) {
			if sub := rLine.FindStringSubmatch(line); len(sub) > 3 {
				list = append(list, jsonObject{"name": sub[1], "address": sub[2], "size": sub[3]})
			}
		}
	}

	m := make(jsonObject)
	m["type"] = "bootloader_load"
	m["list"] = list

	chanOutput <- m
}

func removeBrackets(s string) string {
	for strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")") {
		openCount := strings.Count(s, "(")
		closeCount := strings.Count(s, ")")

		if openCount > closeCount {
			s = strings.TrimPrefix(s, "(")
		} else if openCount < closeCount {
			s = strings.TrimSuffix(s, ")")
		} else {
			s = strings.TrimPrefix(s, "(")
			s = strings.TrimSuffix(s, ")")
		}
	}

	return s
}

func removeTimestamp(s string) string {
	if reTimestamp.MatchString(s) {
		t := reTimestamp.FindString(s)
		s = strings.TrimPrefix(s, t)
	}
	return strings.TrimSpace(s)
}

func extract(from string, chunk, prefix, suffix string) string {
	reChunk, err := regexp.Compile(chunk)
	if err != nil {
		return ""
	}

	rePrefix, err := regexp.Compile(prefix)
	if err != nil {
		return ""
	}

	reSuffix, err := regexp.Compile(suffix)
	if err != nil {
		return ""
	}

	b := reChunk.FindString(from)

	p := rePrefix.FindString(b)
	b = strings.TrimPrefix(b, p)

	s := reSuffix.FindString(b)
	b = strings.TrimSuffix(b, s)

	return b
}