package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"net"

	"github.com/golang/glog"
)

var (
	saddr = flag.String("saddr", ":11143", "address to listen on")
	caddr = flag.String("caddr", "localhost:10143", "remote address to connect to")
)

// scanLines reimplements bufio.ScanLines without eating '\r' in the input.
func scanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, data[0:i], nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), data, nil
	}
	// Request more data.
	return 0, nil, nil
}

func prefixCopy(prefix string, src, dst net.Conn) {
	defer src.Close()
	defer dst.Close()

	glog.Infof("%s proxying %s -> %s", prefix, src.LocalAddr(), dst.RemoteAddr())
	scanner := bufio.NewScanner(src)
	scanner.Split(scanLines)
	for scanner.Scan() {
		glog.Infoln(prefix, scanner.Text()) // Println will add back the final '\n'
		if _, err := fmt.Fprintln(dst, scanner.Text()); err != nil {
			glog.Errorf("%s error writing to dst: %v", prefix, err)
			return
		}
	}
	if err := scanner.Err(); err != nil {
		glog.Errorf("%s error reading from src: %v", prefix, err)
		return
	}
}

func proxy(id int, client net.Conn, remoteAddr string) {
	remote, err := net.Dial("tcp", remoteAddr)
	if err != nil {
		glog.Errorf("Failed to dial %s: %v", remoteAddr, err)
		return
	}
	go prefixCopy(fmt.Sprintf("[%d] S:", id), remote, client)
	go prefixCopy(fmt.Sprintf("[%d] C:", id), client, remote)
}

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

	glog.Infof("Proxying %s -> %s", *saddr, *caddr)
	ln, err := net.Listen("tcp", *saddr)
	if err != nil {
		glog.Exitf("Failed to listen on %s: %v", *saddr, err)
	}
	var id int
	for {
		conn, err := ln.Accept()
		if err != nil {
			glog.Exitf("Failed to accept on %s: %v", *saddr, err)
		}
		proxy(id, conn, *caddr)
		id++
	}
}
