package upload

import (
	"archive/zip"
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/astaxie/beego/orm"

	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/http/cookiejar"
	"os"
	"path"
	"ppt_server/models"
	"regexp"

	Qurl "net/url"
	"strconv"
	"strings"
	"time"
)


var (
	//WEBURL string = "http://offcn-live-svc"
	WEBURL string = "https://test-live.offcncloud.com"
	client *http.Client
	token string
)

const docUrl string = "http://doc.offcncloud.com/"





//func main() {


//tick := time.NewTicker(30 * time.Minute)
//upload()
//for {
//	select {
//	case <-tick.C:
//		upload()
//	}
//}

//}
func ptoken() {
	qclient, qtoken, err := webToken()
	client = qclient
	token  = qtoken
	println("token ===", token)
	if err != nil {
		fmt.Println(err)
		return
	}
}

func Upload(v *models.XyuSmallTempFiles) {

		//ptoken()

		go Down(v)


}

func Down(v *models.XyuSmallTempFiles)  {
	o := orm.NewOrm()

	url, _ := Qurl.QueryUnescape(v.Url)
	filename := path.Base(url)
	filePath := "/data/files/" + filename

	// 文件以及存在就不需要下载
	if _, err := os.Stat(filePath); err != nil {
		// Get the data
		resp, err := http.Get(url)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer resp.Body.Close()
		// 创建一个文件用于保存
		out, err := os.Create(filePath)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer out.Close()

		// 然后将响应流和文件流对接起来
		_, err = io.Copy(out, resp.Body)
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	var room models.XyuRoom

	err := o.QueryTable("xyu_room").Filter("room_num", v.RoomNum).One(&room)
	if err != nil {
		fmt.Println(err)
		return
	}
	bucket, err := newOssClient()
	if err != nil {
		fmt.Println(err)
		return

	}

	file, err := os.Open(filePath)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()
	room_file := make(map[string]interface{})
	t := time.Now()
	now := fmt.Sprintf("%02d-%02d-%02d %02d:%02d:%02d",
		t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
	room_file["room_id"] = room.Id
	room_file["id"] = t.Unix()
	room_file["is_courseware"] = "1"
	room_file["private"] = "1"
	room_file["nickname"] = "助教"
	room_file["name"] = filename
	room_file["time"] = now
	if v.Type == 1 {
		room_file["is_material"] = 1
	} else if v.Type == 2 {
		room_file["is_courseware"] = 1
	} else if v.Type == 3 {
		room_file["is_titlebook"] = 1
	}

	contentHash, _ := fileContentHash(file)
	fileHash := []byte(filename)

	fileHashName := fmt.Sprintf("%x", sha1.Sum(fileHash))
	room_file["file_name_hash"] = fileHashName
	room_file["hash"] = contentHash

	//获取文件后缀
	ext := path.Ext(filename)
	var doc_type string
	doc_type = "0"

	if strings.Contains(ext, "ppt") || strings.Contains(ext, "pptx") {
		doc_type = "4"
	} else if strings.Contains(ext, "doc") || strings.Contains(ext, "docx") {
		doc_type = "2"
	} else if strings.Contains(ext, "xls") || strings.Contains(ext, "xlsx") {
		doc_type = "3"
	} else if strings.Contains(ext, "pdf") {
		doc_type = "1"
	}
	room_file["doc_type"] = doc_type
	_ = mime.AddExtensionType(".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation")
	room_file["type"] = mime.TypeByExtension(ext)

	fd, err := os.Open(filePath)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer fd.Close()
	objectPath := "documents/" + v.RoomNum + "/" + contentHash + "/" + fileHashName + "/"
	objectName := objectPath + contentHash + ext
	fmt.Println("objectName == ", objectName)
	room_file["path"] = objectPath
	err = bucket.PutObject(objectName, fd)
	if err != nil {
		fmt.Println(err)
		return
	}
	furl := "https://xiaoyu-live.oss-cn-beijing.aliyuncs.com/" + objectName
	maps, err := getFileInfo(furl)
	if err != nil {
		fmt.Println(err)
		return
	}

	room_file["files_size"] = strconv.Itoa(int(maps["FileSize"].(float64)))
	room_file["link"] = furl

	wordFloat64 := getWords(maps)

	downUrl := fmt.Sprintf("http://doc.offcncloud.com/?info=1&words=%v&ssl=1&furl=%s",
		wordFloat64, furl)
	go func() {
		err := uploadPacked(bucket, filename, contentHash, objectPath,
			wordFloat64)
		if err != nil {
			fmt.Println(err)
			return
		}
	}()

	ch := make(chan bool)
	go func() {
		err := downloadFile(downUrl, func(length, downLen int64) {

		})
		if err == nil {
			ch <- true
		}
	}()
	<-ch

	var uploadImageCount float64

	reader, err := zip.OpenReader( "/data/files/" + contentHash + ".zip")
	if err != nil {
		fmt.Println(err)
		return
	}
	ch1 := make(chan bool)

	for _, file := range reader.File {

		go func(file *zip.File) {
			err := pdfThumbnail(objectPath, file, bucket)
			if err != nil {
				fmt.Println(objectPath + file.Name + "上传失败")
				// 上传缩略图失败,重试3次
				for i := 0; i < 3; i++ {
					err = pdfThumbnail(objectPath, file, bucket)
					if err == nil {
						ch1 <- true
						return
					}
				}
				ch1 <- false
				return
			} else {
				ch1 <- true
				return
			}

		}(file)
	}
	for i := 0; i < int(wordFloat64); i++ {
		t := <-ch1
		if t {
			uploadImageCount++
		}
	}
	defer reader.Close()
	fmt.Println(uploadImageCount, wordFloat64)
	if uploadImageCount != wordFloat64 {
		fmt.Println("上传失败")
		return
	}


	//调用服务端接口写入数据库
	var roomFile models.XyuRoomFiles

	err = o.QueryTable("xyu_room_files").Filter("room_id", room.Id).Filter("name", filename).One(&roomFile)
	if err == orm.ErrNoRows {

		err = createFileDataBase(room_file)
		if err != nil {
			fmt.Println("err==", err)
			return
		}
	}



	elapsed := time.Since(t)
	fmt.Println("filename == ", filename, " 上传成功, 上传耗时 == ", elapsed)

	// 删除压缩包和删除文件
	defer os.Remove(filePath)
	defer os.Remove("/data/files/" + contentHash+".zip")
	return




}

func webToken() (*http.Client, string, error) {
	jar, err := cookiejar.New(nil)
	if err != nil {
		return &http.Client{}, "", err
	}
	client := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
		Jar: jar,
	}


	payload := strings.NewReader("name=offcn&password=123123")

	req, err := http.NewRequest("POST", WEBURL+"/web/login", payload)
	if err != nil {
		return &http.Client{}, "", err
	}
	req.Header.Add("content-type", "multipart/form-data")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	res, err := client.Do(req)

	if err != nil {
		fmt.Println("token请求失败", err)
		return &http.Client{}, "", err
	}

	bodys, err := ioutil.ReadAll(res.Body)
	if err != nil {
		fmt.Println("获取数据失败",err)
		return &http.Client{}, "", err
	}

	defer res.Body.Close()

	response := string(bodys)

	map2 := make(map[string]interface{})
	err = json.Unmarshal([]byte(response), &map2)
	if err != nil {
		return &http.Client{}, "", err
	}
	if int(map2["code"].(float64)) == 200 {
		//登录成功获取token
		t_res, err := client.Get(WEBURL + "/web/admin")
		if err != nil {
			fmt.Println("token无效", err)
			return &http.Client{}, "", err
		}
		defer t_res.Body.Close()
		body, err := ioutil.ReadAll(t_res.Body)
		if err != nil {
			return &http.Client{}, "", err
		}
		re := regexp.MustCompile(`<meta name="csrf-token" content="([^"]+)">`)
		token := re.FindAllStringSubmatch(string(body), -1)[0][1]

		return client, token, nil
	} else {
		return &http.Client{}, "", errors.New("登录失败")
	}
}

func createFileDataBase(room_file map[string]interface{}) error {

	vv := Qurl.Values{}

	for k, v := range room_file {
		vv.Add(k, fmt.Sprintf("%v", v))
	}
	client, token, err := webToken()
	if err != nil {
		fmt.Println("token 获取失败", err)
		return err
	}

	vv.Add("_token", token)
	body := vv.Encode()
	payload := strings.NewReader(body)
	req, err := http.NewRequest("POST",WEBURL + "/web/room_files_add", payload)

	if err != nil {
		fmt.Println("123")
		return err
	}
	req.Header.Add("content-type", "multipart/form-data")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	res, err := client.Do(req)
	if err != nil {
		fmt.Println("5555")
		return err
	}
	defer res.Body.Close()

	post_body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		fmt.Println("456")
		return err
	}
	map2 := make(map[string]interface{})
	err = json.Unmarshal(post_body, &map2)
	if err != nil {
		fmt.Println("678")
		return err
	}

	if int(map2["code"].(float64)) != 200 {
		return errors.New("上传失败")
	}
	return nil
}

func pdfThumbnail(objectPath string, file *zip.File, bucket *oss.Bucket) error {

	fc, err := file.Open()
	if err != nil {
		return err
	}
	defer fc.Close()
	objectPathName := objectPath + "images/"
	var fileSuffix string
	fileSuffix = path.Ext(file.Name) //获取文件后缀

	var filenameOnly string
	filenameOnly = strings.TrimRight(file.Name, fileSuffix)
	filenameOnly = strings.Replace(filenameOnly, "p", "", -1)
	fileName := fmt.Sprintf("%05s", filenameOnly) + fileSuffix

	return bucket.PutObject(objectPathName+fileName, fc)

}


func uploadPacked(bucket *oss.Bucket, filename, contentHash,
	objectPath string, word float64) error {

	type packed struct {
		FileName      string   `json:"fileName"`
		Hash          string   `json:"hash"`
		ImagesName    []string `json:"imagesName"`
		OssImagesPath string   `json:"ossImagesPath"`
	}
	p := packed{
		FileName:      filename,
		Hash:          contentHash,
		OssImagesPath: objectPath + "images/",
	}
	for i := 1; i <= int(word); i++ {
		p.ImagesName = append(p.ImagesName, fmt.Sprintf("%05d", i)+".png")
	}
	data, err := json.Marshal(p)
	if err != nil {
		return err
	}

	err = bucket.PutObject(objectPath+"packed.json",
		strings.NewReader(string(data)))
	if err != nil {
		return err
	}
	return nil
}


func getWords(maps map[string]interface{}) float64 {
	if word, ok := maps["SlideCount"]; ok {
		return word.(float64)
	}
	if word, ok := maps["PageCount"]; ok {
		return word.(float64)
	}
	if word, ok := maps["SheetCount"]; ok {
		return word.(float64)
	}
	return float64(0)
}

func getFileInfo(ossFile string) (map[string]interface{}, error) {
	url := docUrl + "?info=0&ssl=1&furl=" + ossFile

	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}
	var maps map[string]interface{}

	err = json.Unmarshal(b, &maps)
	if err != nil {
		return nil, err
	}
	return maps, nil
}

func newOssClient() (*oss.Bucket, error) {
	client, err := oss.New("oss-cn-beijing.aliyuncs.com",
		"LTAI1fMvVUPBXl2E", "cTAMLufmPFznfE0peur8oMmy2c5kvk")
	if err != nil {
		return nil, err
	}
	bucket, err := client.Bucket("xiaoyu-live")

	if err != nil {
		return nil, err
	}
	return bucket, nil
}

func fileContentHash(file io.Reader) (string, []byte) {
	b, err := ioutil.ReadAll(file)
	if err != nil {
		fmt.Println(err)
	}
	hash := md5.New()
	io.WriteString(hash, string(b))
	result := hash.Sum(nil)
	return fmt.Sprintf("%s", hex.EncodeToString(result)), b
}

func downloadFile(url string, fb func(length, downLen int64)) error {
	var (
		fsize   int64
		buf     = make([]byte, 32*1024)
		written int64
	)
	//创建一个http client
	client := new(http.Client)
	//get方法获取资源
	resp, err := client.Get(url)
	fmt.Println(url)
	if err != nil {
		return err
	}
	//读取服务器返回的文件大小
	fsize, err = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 32)
	if err != nil {
		fmt.Println(err)
	}
	//创建文件
	var filenameWithSuffix string
	filenameWithSuffix = path.Base(url) //获取文件名带后缀

	var fileSuffix string
	fileSuffix = path.Ext(filenameWithSuffix) //获取文件后缀

	var filenameOnly string
	filenameOnly = strings.Replace(filenameWithSuffix, fileSuffix, ".zip", -1) //获取文件名
	file, err := os.Create("/data/files/" + filenameOnly)
	if err != nil {
		return err
	}
	defer file.Close()
	if resp.Body == nil {
		return errors.New("body is null")
	}
	defer resp.Body.Close()
	//下面是 io.copyBuffer() 的简化版本
	for {
		//读取bytes
		nr, er := resp.Body.Read(buf)
		if nr > 0 {
			//写入bytes
			nw, ew := file.Write(buf[0:nr])
			//数据长度大于0
			if nw > 0 {
				written += int64(nw)
			}
			//写入出错
			if ew != nil {
				err = ew
				break
			}
			//读取是数据长度不等于写入的数据长度
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
		if er != nil {
			if er != io.EOF {
				err = er
			}
			break
		}
		//没有错误了快使用 callback

		fb(fsize, written)
	}
	return err
}
