package main

import (
	"fmt"
	"os"
	"sync"
	dbutil "tiloader/pkg/dbutil"
	errutil "tiloader/pkg/errutil"
	fileutil "tiloader/pkg/fileutil"
	log "tiloader/pkg/logutil"
	"time"
)

var batchSql string
var version = "1.0"

func init() {

}

func main() {
	// 当前时间
	start := time.Now()
	defer func() {
		cost := time.Since(start)
		log.WriteLog("cost time:" + cost.String())
	}()

	//从启动参数读取config
	cfgFilePath := parseParam()

	//读取配置文件
	cfgParser := new(fileutil.ConfigFileParser)
	configFile, err := cfgParser.Parse(cfgFilePath)
	if err != nil {
		log.Error(err.Error())
		return
	}
	errutil.Init(configFile)

	//初始化log
	log.InitLog(configFile.LogFile, configFile.ErrFile)

	//解析描述文件
	descFilePath := configFile.DescFilePath //从配置文件读取
	descParser := new(fileutil.DescFileParser)
	descFile, err := descParser.Parse(descFilePath)
	if err != nil {
		log.Error(err.Error())
		return
	}

	var wg sync.WaitGroup

	//解析数据文件，把数据按行放进管道
	dataParser := new(fileutil.DataFileParser)
	// wg.Add(1)
	//行数据管道
	// readChan := make(chan string, configFile.WorkerNum*100)
	var readChanArray []chan string = make([]chan string, configFile.WorkerNum)
	for i := 0; i < configFile.WorkerNum; i++ {
		readChanArray[i] = make(chan string, configFile.BatchSize)
	}
	dataParser.ReadRows2(descFile, int64(configFile.SkipLine), readChanArray[:], &wg, configFile.WorkerNum)

	//sql语句管道
	// sqlChan := make(chan dbutil.SqlInfo, configFile.WorkerNum*100)
	// var sqlChanArray [10]chan dbutil.SqlInfo
	var sqlChanArray []chan dbutil.SqlInfo = make([]chan dbutil.SqlInfo, configFile.WorkerNum)
	for i := 0; i < configFile.WorkerNum; i++ {
		sqlChanArray[i] = make(chan dbutil.SqlInfo, configFile.BatchSize)
	}

	if descFile.Method == fileutil.REPLACE {
		dbutil.Truncate(descFile.TableName, configFile)
	}

	//按行把数据转化成sql，放进管道
	genSqlInPool2(readChanArray[:], descFile, sqlChanArray[:], &wg, configFile)

	//通过协程池并发执行sql语句
	execSqlInPool2(sqlChanArray[:], configFile, &wg, descFile)
	wg.Wait()
}

//demo flagset usage
func parseParam() (paramVal string) {
	if len(os.Args) <= 1 {
		fmt.Println("param count error.")
		os.Exit(2)
	}
	if os.Args[1] == "--version" || os.Args[1] == "-version" || os.Args[1] == "-v" {
		fmt.Println(version)
		os.Exit(0)
	} else if (os.Args[1] != "-config" && os.Args[1] != "--config") || len(os.Args) < 3 {
		fmt.Println("param error.")
		os.Exit(2)
	}
	//fmt.Println(os.Args[2])
	return os.Args[2]

}

func genSqlInPool2(readChan []chan string, descFile fileutil.DescFile, sqlChan []chan dbutil.SqlInfo, wg *sync.WaitGroup, cfg fileutil.ConfigFile) {
	log.WriteLog("genSqlInPool begin")
	// genSqlPool := pool.NewWorkerPool(uint(cfg.WorkerNum), "genSqlPool")

	for i := 0; i < cfg.WorkerNum; i++ {
		finalI := i
		wg.Add(1)
		go func() {
			var sqlInfo dbutil.SqlInfo
			var batchSize = cfg.BatchSize
			//param := make([]string, len(descFile.Cols)*batchSize)

			// genSqlPool.Apply(func() {
			count := 0
			for rd := range readChan[finalI] {
				rowData := rd
				var sqlHalf2Temp string
				//log.WriteLog("rowData:" + rowData)
				lenRowData := len(rowData)
				positionEnd := descFile.Cols[len(descFile.Cols)-1].PositionEnd
				if lenRowData < positionEnd {
					//如果这行数据短于描述文件的位长，直接跳过并把这行数据写入错误日志
					log.WriteLog("skip error data:" + rowData)
					log.Error("insufficient data length:" + rowData)
					continue
				}
				if count == 0 {
					sqlInfo = dbutil.GenSql(rowData, descFile)
					sqlHalf2Temp = sqlInfo.SqlHalf2

					myparam := make([]string, len(descFile.Cols))
					dbutil.GenData(rowData, descFile, myparam)
					sqlInfo.Param = MergeArray(sqlInfo.Param, myparam)
					count++

				} else if count < (batchSize - 1) {
					//sqlInfoTemp := dbutil.GenSql(rowData, descFile)
					//sqlInfo.SqlHalf2 = sqlInfo.SqlHalf2 + "," + sqlInfoTemp.SqlHalf2

					myparam := make([]string, len(descFile.Cols))
					dbutil.GenData(rowData, descFile, myparam)
					sqlInfo.Param = MergeArray(sqlInfo.Param, myparam)
					count++

				} else if count >= (batchSize - 1) {
					//sqlInfoTemp := dbutil.GenSql(rowData, descFile)
					//sqlInfo.SqlHalf2 = sqlInfo.SqlHalf2 + "," + sqlInfoTemp.SqlHalf2

					myparam := make([]string, len(descFile.Cols))
					dbutil.GenData(rowData, descFile, myparam)
					sqlInfo.Param = MergeArray(sqlInfo.Param, myparam)
					sqlChan[finalI] <- sqlInfo

					//达到batch size后，count归零，SqlHalf2重新累加
					count = 0
					sqlInfo.SqlHalf2 = sqlHalf2Temp

				}
			}
			if count != 0 {
				sqlChan[finalI] <- sqlInfo
				count = 0
			}
			defer close(sqlChan[finalI])
			defer wg.Done()
			// })
		}()
	}

	// genSqlPool.WaitFinished()

	log.WriteLog("genSqlInPool end")
}

func MergeArray(dest []string, src []string) (result []string) {
	result = make([]string, len(dest)+len(src))

	//将第一个数组传入result
	copy(result, dest)
	//将第二个数组接在尾部，也就是 len(dest):
	copy(result[len(dest):], src)

	return
}

func execSqlInPool2(sqlChan []chan dbutil.SqlInfo, configFile fileutil.ConfigFile, wg *sync.WaitGroup, descFile fileutil.DescFile) {
	log.WriteLog("execPool begin")
	// execSqlPool := pool.NewWorkerPool(uint(configFile.WorkerNum), "execSqlPool")

	for i := 0; i < configFile.WorkerNum; i++ {
		finalI := i
		wg.Add(1)
		go func() {
			count := 0
			for sql := range sqlChan[finalI] {
				count++
				cursql := sql

				if len(cursql.Param) == len(descFile.Cols)*configFile.BatchSize {
					if batchSql == "" {
						sqlHalf2Temp := cursql.SqlHalf2
						for i := 1; i < configFile.BatchSize; i++ {
							cursql.SqlHalf2 += "," + sqlHalf2Temp
						}
						batchSql = cursql.SqlHalf1 + cursql.SqlHalf2
					}
					// fmt.Println("batchSql:" + batchSql)
					_, err := dbutil.Insert(batchSql, cursql.Param, configFile)
					if err != nil {
						fmt.Println(err.Error())
					}
				} else {
					sqlHalf2Temp := cursql.SqlHalf2
					for i := 1; i < len(cursql.Param)/len(descFile.Cols); i++ {
						cursql.SqlHalf2 += "," + sqlHalf2Temp
					}
					lastSql := cursql.SqlHalf1 + cursql.SqlHalf2
					//fmt.Println("lastSql:" + lastSql)
					_, err := dbutil.Insert(lastSql, cursql.Param, configFile)
					if err != nil {
						fmt.Println(err.Error())
					}
				}
			}
			defer wg.Done()

		}()
	}

	// execSqlPool.WaitFinished()

	log.WriteLog("execPool end")
}
