package main

import (
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

func getPort(default_port string) string {
	port := os.Getenv("PORT")

	if port == "" {
		return default_port
	}

	return port
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Type", "text/html")
	http.ServeFile(w, r, "./index.html")
}

const UPLOADS_FOLDER_PREFIX = "./uploads/"

func saveFilesOnDisk(files []*multipart.FileHeader) (folderPath string, filesPaths []string, err error) {
	// Create folder for uploaded files

	folderPath = UPLOADS_FOLDER_PREFIX + strconv.FormatInt(time.Now().Unix(), 10)

	if err := os.MkdirAll(folderPath, os.ModePerm); err != nil {
		return "", filesPaths, err
	}

	filesPaths = make([]string, len(files))

	for i, fileHeader := range files {
		// Open file from request
		file, err := fileHeader.Open()
		if err != nil {
			return "", filesPaths, err
		}

		defer file.Close()

		// Generate and store file name

		fileName := strconv.Itoa(i) + strings.ToLower(filepath.Ext(fileHeader.Filename))

		filesPaths[i] = folderPath + "/" + fileName

		// Create file on disk
		dst, err := os.Create(filesPaths[i])
		if err != nil {
			return "", filesPaths, err
		}

		defer dst.Close()

		_, err = io.Copy(dst, file)
		if err != nil {
			return "", filesPaths, err
		}
	}

	return folderPath, filesPaths, nil
}

type ContentType int

const (
	Images ContentType = iota
	Office
	Pdfs
)

func detectContentType(files []*multipart.FileHeader) (contentType ContentType, err error) {
	for _, file := range files {
		fileExt := strings.ToLower(filepath.Ext(file.Filename))
		switch fileExt {
		case ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".odt", ".ods", ".odp":
			if len(files) == 1 {
				return Office, nil
			} else {
				return contentType, fmt.Errorf("expected one document, got %d", len(files))
			}
		case ".jpg", ".jpeg", ".png":
			contentType = Images
		case ".pdf":
			contentType = Pdfs
		default:
			return contentType, fmt.Errorf("%s file extension is unsupported yet", fileExt)
		}
	}

	return contentType, nil
}

const MAX_UPLOAD_SIZE = (1 << 27)

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	// Restrict to POST http method
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Restrict max size of a bunch of files
	r.Body = http.MaxBytesReader(w, r.Body, MAX_UPLOAD_SIZE)

	// Parse body
	if err := r.ParseMultipartForm(MAX_UPLOAD_SIZE); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	files := r.MultipartForm.File["files"]

	folderPath, filesPaths, err := saveFilesOnDisk(files)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	defer os.RemoveAll(folderPath)

	contType, err := detectContentType(files)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	resFile, err := ConvertToPDF(folderPath, filesPaths, contType)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Disposition", "attachment; filename=\""+filepath.Base(resFile)+"\"")

	http.ServeFile(w, r, resFile)
}

func main() {
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/upload", uploadHandler)

	port := getPort("80")

	fmt.Printf("Listening on %s\n", port)

	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}