package recordview

import (
	"dbweb/core"
	"dbweb/lib/safe"
	"dbweb/lib/strfun"
	"encoding/base64"
	"sort"
	"strings"

	"github.com/linlexing/dbx/scan"

	"github.com/pborman/uuid"
)

//RecordViewParam 定义存储结构，其存放在数据库中
type RecordViewParam struct {
	user        *core.User
	newID       string
	DB          string
	Sql         string
	ManualPage  bool //是否手工分页，手工分页将用template渲染sql，而不会嵌套sql进行分页
	TableName   string
	UniqueField []string         `yaml:",flow"`
	Columns     []*DisplayColumn 
	Processes   []*RecordViewProcess
	AllColumns  []*scan.ColumnType 
}

//FindColumn 返回一个指定名称的列
func (r *RecordViewParam) FindColumn(name string) *scan.ColumnType {
	for _, v := range r.AllColumns {
		if v.Name == name {
			return v
		}
	}
	return nil
}

//RenderRows 返回一个用于渲染的Rows
func (r *RecordViewParam) renderRows(rows []map[string]interface{}, selKeys []string, divideRowNum int64) []*RenderRow {
	renderRows := []*RenderRow{}
	for i, v := range rows {
		//最后一行不用显示
		//需要计算出每行的唯一主键值，并且得出是否被选中
		key := []string{}
		for _, oneKey := range r.UniqueField {
			key = append(key, safe.String(v[oneKey]))
		}
		enkey := strfun.EncodeCSV(key)
		idx := sort.SearchStrings(selKeys, enkey)
		checked := idx < len(selKeys) && selKeys[idx] == enkey
		renderRows = append(renderRows, &RenderRow{int64(i) + divideRowNum, checked, enkey, v})
	}
	return renderRows
}

//BuildProcessParamToSession 调用后，自动生成ProcessParam信息，并置入Session中
func (r *RecordViewParam) BuildProcessParamToSession(p *core.ElementHandleArgs,
	sqlSelect *RecordSelect, selKeys, hideCols []string) {
	if len(r.Processes) > 0 {
		bfound := false
		for _, o := range r.Processes {
			if o.WithSql {
				bfound = true
			}
		}
		if bfound {
			r.newID = base64.RawURLEncoding.EncodeToString(uuid.NewUUID())
			//用于传递的sql不需要条数限制
			p.LSession.Set(r.newID, &ProcessParam{
				Select:      sqlSelect,
				FromElement: p.Element.Name,
				SelKeys:     selKeys,
				HideCols:    hideCols,
			})
		}
	}
}

//QueryRender 返回一个查询结果，包含所有必要的信息
func (r *RecordViewParam) QueryRender(s *RecordSelect, selKeys []string, divideRowNum int64) (*RenderResult, error) {
	sqlSelect, hideCols := s.getSelect()
	db := core.LoadOuterDB(s.p.DB, s.DBName)
	rows, cols, err := sqlSelect.QueryRows(db.DriverName(), db)
	if err != nil {
		return nil, err
	}
	r1 := rows
	if len(rows) > s.Limit {
		r1 = r1[:len(r1)-1]
	}
	rev := &RenderResult{
		Rows:           r.renderRows(r1, selKeys, divideRowNum),
		Columns:        []*scan.ColumnType{},
		DisplayColumns: []*scan.ColumnType{},
	}
	for _, col := range cols {
		fcol := r.FindColumn(col.Name)
		if fcol == nil {
			fcol = &scan.ColumnType{
				Name: col.Name,
				Type: col.Type,
			}
		}
		rev.Columns = append(rev.Columns, fcol)
		//显示字段需要去掉隐藏字段
		hide := false
		for _, s := range hideCols {
			if s == fcol.Name {
				hide = true
				break
			}
		}
		if !hide {
			rev.DisplayColumns = append(rev.DisplayColumns, fcol)
		}
	}
	//生成下一页的参数
	if len(rows) > s.Limit {
		rev.DownDivide = getDivideValues(rev.Rows[len(rev.Rows)-1].Data, sqlSelect.Order)
		rev.DownRowNum = rev.Rows[len(rev.Rows)-1].RowNum
	}
	//如果不满限制或刚好等于限制的记录数，则总数就是最后一行的序号，因为多取了一行，所以可以用等于
	if len(rows) <= s.Limit {
		if len(rows) == 0 {
			rev.RowCount = 0
		} else {
			rev.RowCount = rev.Rows[len(rev.Rows)-1].RowNum
		}
	}
	return rev, nil

}
func getDivideValues(row map[string]interface{}, order []string) string {
	r := []string{}
	for _, s := range order {
		if strings.HasPrefix(s, "-") {
			r = append(r, safe.String(row[s[1:]]))
		} else {
			r = append(r, safe.String(row[s]))
		}
	}
	return strfun.EncodeCSV(r)
}
