package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"mime"
	"net/mail"
	"sort"
	"xinu.tv/email/db"

	"github.com/golang/glog"
)

var (
	dumpMime = flag.Bool("mime", false, "show multipart mime types for message")
	headers  = flag.Bool("headers", false, "show only headers")
)

func printMultipartMime(hash string, r io.Reader) error {
	msg, err := mail.ReadMessage(r)
	if err != nil {
		return err
	}

	mediatype, params, err := mime.ParseMediaType(msg.Header.Get("Content-Type"))
	if err != nil {
		return err
	}
	glog.Infof("%s mt: %s p: %v", hash, mediatype, params)

	return nil
}

func printHeaders(hash string, r io.Reader) error {
	msg, err := mail.ReadMessage(r)
	if err != nil {
		return err
	}
	hdrs := make([]string, 0, len(msg.Header))
	for k := range msg.Header {
		hdrs = append(hdrs, k)
	}
	fmt.Println(hash)
	sort.Strings(hdrs)
	for _, k := range hdrs {
		vs := msg.Header[k]
		fmt.Printf("%s:", k)
		for _, v := range vs {
			fmt.Printf(" %s", v)
		}
		fmt.Println()
	}
	return nil
}

func printOneBlob(hash string, blob []byte) error {
	r := bytes.NewReader(blob)
	switch {
	case *dumpMime:
		if err := printMultipartMime(hash, r); err != nil {
			// err != nil expected when mime type not found, so we log and
			// squelch the error.
			glog.Warningf("%s: %v", hash, err)
			return nil
		}
	case *headers:
		if err := printHeaders(hash, r); err != nil {
			return err
		}
	default:
		fmt.Println(string(blob))
	}
	return nil
}

type hashError struct {
	hash string
	error
}

func (he hashError) Error() string {
	return fmt.Sprintf("%s: %v", he.hash, he.error)
}

func printAllBlobs(c *db.Conn) error {
	oCh := make(chan db.Original)
	errc := make(chan error)
	donec := make(chan struct{})
	defer close(donec)
	go c.Originals(oCh, errc, donec)
	for {
		select {
		case o := <-oCh:
			if err := printOneBlob(o.Hash, o.Blob); err != nil {
				return hashError{hash: o.Hash, error: err}
			}
		case err := <-errc:
			return err
		}
	}
}

func main() {
	defer glog.Flush()
	flag.Parse()

	c, err := db.NewConn("")
	if err != nil {
		glog.Fatal(err)
	}

	if flag.NArg() == 0 {
		if err := printAllBlobs(c); err != nil {
			glog.Fatal(err)
		}
		return
	}

	var blob []byte
	for _, hash := range flag.Args() {
		if err := c.OriginalBlobByHash(hash, &blob); err != nil {
			glog.Fatal(err)
		}
		if err := printOneBlob(hash, blob); err != nil {
			glog.Fatal(err)
		}
	}
}
