package p2p

import (
    "bytes"
    "errors"
    "github.com/Akilan1999/p2p-rendering-computation/config"
    "io"
    "io/ioutil"
    "log"
    "mime/multipart"
    "net/http"
    "os"
    "time"
)

// var dlSizes = [...]int{350, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000}
// var ulSizes = [...]int{100, 300, 500, 800, 1000, 1500, 2500, 3000, 3500, 4000} //kB
var httpclient = http.Client{}

// DownloadTest executes the test to measure download speed
//func (s *IpAddress) DownloadTest(savingMode bool) error {
//	dlURL := "http://" + s.Ipv4 + ":8088/server_info"
//	eg := errgroup.Group{}
//
//	// Warming up
//	sTime := time.Now()
//	for i := 0; i < 2; i++ {
//		eg.Go(func() error {
//			return dlWarmUp("http://" + s.Ipv4 + ":8088/server_info")
//		})
//	}
//	if err := eg.Wait(); err != nil {
//		return err
//	}
//	fTime := time.Now()
//	// 1.125MB for each request (750 * 750 * 2)
//	wuSpeed := 1.125 * 8 * 2 / fTime.Sub(sTime.Add(s.Latency)).Seconds()
//
//	// Decide workload by warm up speed
//	workload := 0
//	weight := 0
//	skip := false
//	if savingMode {
//		workload = 6
//		weight = 3
//	} else if 10.0 < wuSpeed {
//		workload = 16
//		weight = 4
//	} else if 4.0 < wuSpeed {
//		workload = 8
//		weight = 4
//	} else if 2.5 < wuSpeed {
//		workload = 4
//		weight = 4
//	} else {
//		skip = true
//	}
//
//	// Main speedtest
//	dlSpeed := wuSpeed
//	if skip == false {
//		sTime = time.Now()
//		for i := 0; i < workload; i++ {
//			eg.Go(func() error {
//				return downloadRequest(dlURL, weight)
//			})
//		}
//		if err := eg.Wait(); err != nil {
//			return err
//		}
//		fTime = time.Now()
//
//		reqMB := dlSizes[weight] * dlSizes[weight] * 2 / 1000 / 1000
//		dlSpeed = float64(reqMB) * 8 * float64(workload) / fTime.Sub(sTime).Seconds()
//	}
//
//	s.Download = dlSpeed
//	return nil
//}

// Download Speed
func (s *IpAddress) DownloadSpeed() error {
    start := time.Now()
    resp, err := httpclient.Get("http://" + s.Ipv4 + ":8088/50")
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    ioutil.ReadAll(resp.Body)
    t := time.Since(start)
    //fmt.Println(s.Seconds())
    // size * time (seconds)
    s.Download = (50 / t.Seconds()) * 8
    return nil
}

func (s *IpAddress) UploadSpeed() error {
    start := time.Now()

    // Get upload file path from config file
    // Get Path from config
    config, err := config.ConfigInit(nil, nil)
    if err != nil {
        return err
    }

    b, w := createMultipartFormData("file", config.SpeedTestFile)

    req, err := http.NewRequest("GET", "http://"+s.Ipv4+":"+s.ServerPort+"/upload", &b)
    if err != nil {
        return err
    }
    // Don't forget to set the content type, this will contain the boundary.
    req.Header.Set("Content-Type", w.FormDataContentType())
    defer req.Body.Close()
    ioutil.ReadAll(req.Body)
    t := time.Since(start)
    //fmt.Println(s.Seconds())
    // size * time (seconds)
    s.Upload = (50 / t.Seconds()) * 8
    return nil
}

// Upload helper function for uploading
// (https://stackoverflow.com/questions/20205796/post-data-using-the-content-type-multipart-form-data
func createMultipartFormData(fieldName, fileName string) (bytes.Buffer, *multipart.Writer) {
    var b bytes.Buffer
    var err error
    w := multipart.NewWriter(&b)
    var fw io.Writer
    file := mustOpen(fileName)
    if fw, err = w.CreateFormFile(fieldName, file.Name()); err != nil {
        log.Fatalf("Error creating writer: %v", err)
    }
    if _, err = io.Copy(fw, file); err != nil {
        log.Fatalf("Error with io.Copy: %v", err)
        //t.Errorf("Error with io.Copy: %v", err)
    }
    w.Close()
    return b, w
}

func mustOpen(f string) *os.File {
    r, err := os.Open(f)
    if err != nil {
        log.Fatalf("Error with mustOpen: %v", err)
    }
    return r
}

// PingTest executes test to measure latency
func (s *IpAddress) PingTest() error {
    //pingURL := strings.Split(s.URL, "/upload")[0] + "/latency.txt"
    var pingURL string
    if s.Ipv6 != "" {
        pingURL = "http://[" + s.Ipv6 + "]:" + s.ServerPort + "/server_info"
        s.Ipv4 = ""
    } else {
        pingURL = "http://" + s.Ipv4 + ":" + s.ServerPort + "/server_info"
    }

    l := time.Duration(100000000000) // 10sec
    for i := 0; i < 3; i++ {
        sTime := time.Now()
        resp, err := http.Get(pingURL)
        fTime := time.Now()
        if err != nil || resp.StatusCode != 200 {
            return errors.New("Node not found")
        }
        if fTime.Sub(sTime) < l {
            l = fTime.Sub(sTime)
        }
        resp.Body.Close()
    }

    s.Latency = time.Duration(int64(l.Nanoseconds() / 2))

    return nil
}
