package main

import (
	"os"
	"net/http"
	"io"
	"path/filepath"
	"encoding/json"
	"github.com/go-redis/redis"
	"fmt"
	"encoding/base64"
	"crypto/rand"
)

func writeBody(reader io.ReadCloser, path string) error {
	out, err := os.Create(path)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, reader)
	if err != nil {
		return err
	}
	return out.Close()
}

func detectMimeType(path string) (string, error) {
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}

	buffer := make([]byte, 512)
	_, err = file.Read(buffer)
	if err != nil {
		return "", err
	}

	return http.DetectContentType(buffer), nil
}

func generateId() string {
	buffer := make([]byte, 4)
	rand.Read(buffer)

	return base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(buffer)
}

func createImage(config *Config, body io.ReadCloser) (Image, error) {
	id := generateId()
	path := filepath.Join(config.SourceFolder, id)

	err := writeBody(body, path)
	if err != nil {
		return Image{}, err
	}

	mimeType, err := detectMimeType(path)
	if err != nil {
		return Image{}, err
	}

	image := Image{
		Id:       id,
		MimeType: mimeType,
	}
	return image, nil
}

func returnResult(writer http.ResponseWriter, result Result) error {
	writer.Header().Add("Content-Type", "text/html")
	body, err := json.Marshal(result)
	if err != nil {
		return err
	}
	writer.Write([]byte("<pre>"))
	writer.Write(body)
	writer.Write([]byte("</pre>"))
	if result.Success {
		writer.Write([]byte("<p><a href=\""))
		writer.Write([]byte(fmt.Sprintf("http://localhost:8080/i/%s", result.Id)))
		writer.Write([]byte("\">Uploaded Image</a></p>"))
	}
	return nil
}

func main() {
	config := Config{}

	json.Unmarshal([]byte(os.Getenv("IK8R_SIZES")), &config.Sizes)
	json.Unmarshal([]byte(os.Getenv("IK8R_QUALITY")), &config.Quality)
	config.SourceFolder = os.Getenv("IK8R_SOURCE_FOLDER")
	config.TargetFolder = os.Getenv("IK8R_TARGET_FOLDER")
	config.Redis.Address = os.Getenv("IK8R_REDIS_ADDRESS")
	config.ImageQueue = os.Getenv("IK8R_REDIS_IMAGE_QUEUE")
	config.ResultChannel = os.Getenv("IK8R_REDIS_RESULT_CHANNEL")

	client := redis.NewClient(&redis.Options{
		Addr:     config.Redis.Address,
		Password: config.Redis.Password,
	})

	http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
		r.ParseMultipartForm(32 << 20)
		file, _, err := r.FormFile("file")
		image, err := createImage(&config, file)
		if err != nil {
			returnResult(w, Result{
				Id:      "",
				Success: false,
				Errors:  []string{err.Error()},
			})
			return
		}

		data, err := json.Marshal(image)
		if err != nil {
			returnResult(w, Result{
				Id:      image.Id,
				Success: false,
				Errors:  []string{err.Error()},
			})
			return
		}

		pubsub := client.Subscribe(config.ResultChannel)
		client.RPush(fmt.Sprintf("queue:%s", config.ImageQueue), data)

		waiting := true
		for waiting {
			message, err := pubsub.ReceiveMessage()
			if err != nil {
				returnResult(w, Result{
					Id:      image.Id,
					Success: false,
					Errors:  []string{err.Error()},
				})
				return
			}

			result := Result{}
			err = json.Unmarshal([]byte(message.Payload), &result)
			if err != nil {
				returnResult(w, Result{
					Id:      image.Id,
					Success: false,
					Errors:  []string{err.Error()},
				})
				return
			}

			if result.Id == image.Id {
				waiting = false

				returnResult(w, result)
				return
			}
		}
	})

	fs := http.FileServer(http.Dir("static/"))
	http.Handle("/", fs)

	imageServer := http.FileServer(http.Dir(config.TargetFolder))
	http.Handle("/i/", http.StripPrefix("/i/", imageServer))

	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		panic(err)
	}
}