// pp -- KEKS pretty printer
// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"go.cypherpunks.su/keks"
)

func usage() {
	fmt.Fprintf(os.Stderr, `Usage: pp [options] <DATA.keks

`)
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, `
/that/keks/path specifies some exact element of the structure.
For example, /sigs/0/tbs will show only tbs field of the first signature in
cm/pub structure. With "-v -p /sigs/0/tbs/sid" option you will get hexadecimal
value of the signature's sid.
`)
}

var (
	MaxStrLen = flag.Uint("max-str-len", 64,
		"Maximal string length to print")
	MaxParseCycles = flag.Uint64("max-parse-cycles", 1<<63,
		"Maximal number of parse cycles to perform")
	NoTotals = flag.Bool("no-totals", false, "Do not print how many bytes read")
	onlyV    = flag.Bool("v", false, "Print only values")
	pthStr   = flag.String("p", "", "Print only /that/keks/path")
	dumpJSON = flag.Bool("json", false, "Dump as JSON")
	dumpTcl  = flag.Bool("tcl", false, "Dump as Tcl code")

	pth []string
)

func main() {
	flag.Usage = usage
	flag.Parse()
	if *pthStr != "" {
		pth = strings.Split(strings.TrimLeft(*pthStr, "/"), "/")
		*NoTotals = true
	}
	if *onlyV {
		*MaxStrLen = 1 << 62
	}
	if *onlyV || os.Getenv("NO_COLOR") != "" {
		noColour()
	}
	br := bufio.NewReader(os.Stdin)
	var off int64
	var ctx *keks.Decoder
	var err error
	for i := uint64(0); i < *MaxParseCycles; i++ {
		ctx = keks.NewDecoderFromReader(br, &keks.DecodeOpts{SaveOffsets: true})
		ctx.Read = off
		if _, err = ctx.Parse(); err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}
		if *dumpJSON {
			v, err := ctx.Unmarshal()
			if err != nil {
				log.Fatal(err)
			}
			e := json.NewEncoder(os.Stdout)
			e.SetIndent("", "  ")
			err = e.Encode(&v)
			if err != nil {
				log.Fatal(err)
			}
		} else if *dumpTcl {
			tcl(ctx.Iter(), 1, false, false)
		} else {
			printer(ctx.Iter(), []string{}, 1, false, false)
		}
		off = ctx.Read
	}
	if !*NoTotals && !*dumpJSON && !*dumpTcl {
		fmt.Println(off, "bytes")
	}
}
