/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package main

import (
	"bufio"
	"errors"
	"fmt"
	"htrace/common"
	"io"
	"os"
	"sort"
)

// Create a dotfile from a json file.
func jsonSpanFileToDotFile(jsonFile string, dotFile string) error {
	spans, err := readSpansFile(jsonFile)
	if err != nil {
		return errors.New(fmt.Sprintf("error reading %s: %s",
			jsonFile, err.Error()))
	}
	var file *OutputFile
	file, err = CreateOutputFile(dotFile)
	if err != nil {
		return errors.New(fmt.Sprintf("error opening %s for write: %s",
			dotFile, err.Error()))
	}
	defer func() {
		if file != nil {
			file.Close()
		}
	}()
	writer := bufio.NewWriter(file)
	err = spansToDot(spans, writer)
	if err != nil {
		return err
	}
	err = writer.Flush()
	if err != nil {
		return err
	}
	err = file.Close()
	file = nil
	return err
}

// Create output in dotfile format from a set of spans.
func spansToDot(spans common.SpanSlice, writer io.Writer) error {
	sort.Sort(spans)
	idMap := make(map[[16]byte]*common.Span)
	for i := range spans {
		span := spans[i]
		if idMap[span.Id.ToArray()] != nil {
			fmt.Fprintf(os.Stderr, "There were multiple spans listed which "+
				"had ID %s.\nFirst:%s\nOther:%s\n", span.Id.String(),
				idMap[span.Id.ToArray()].ToJson(), span.ToJson())
		} else {
			idMap[span.Id.ToArray()] = span
		}
	}
	childMap := make(map[[16]byte]common.SpanSlice)
	for i := range spans {
		child := spans[i]
		for j := range child.Parents {
			parent := idMap[child.Parents[j].ToArray()]
			if parent == nil {
				fmt.Fprintf(os.Stderr, "Can't find parent id %s for %s\n",
					child.Parents[j].String(), child.ToJson())
			} else {
				children := childMap[parent.Id.ToArray()]
				if children == nil {
					children = make(common.SpanSlice, 0)
				}
				children = append(children, child)
				childMap[parent.Id.ToArray()] = children
			}
		}
	}
	w := NewFailureDeferringWriter(writer)
	w.Printf("digraph spans {\n")
	// Write out the nodes with their descriptions.
	for i := range spans {
		w.Printf(fmt.Sprintf(`  "%s" [label="%s"];`+"\n",
			spans[i].Id.String(), spans[i].Description))
	}
	// Write out the edges between nodes... the parent/children relationships
	for i := range spans {
		children := childMap[spans[i].Id.ToArray()]
		sort.Sort(children)
		if children != nil {
			for c := range children {
				w.Printf(fmt.Sprintf(`  "%s" -> "%s";`+"\n",
					spans[i].Id.String(), children[c].Id))
			}
		}
	}
	w.Printf("}\n")
	return w.Error()
}
