package model

import (
	"fmt"
	"sort"

	xtime "go-common/library/time"
)

const (
	bit1                 = int8(1)
	bit2                 = int8(1) << 1
	StateDefaultPublic   = int8(0)        // binary 00 / int 0
	StateDefaultNoPublic = int8(0) | bit1 // binary 01 / int 1
	StateNormalPublic    = bit2 | int8(0) // binary 10 / int 2
	StateNormalNoPublic  = bit2 | bit1    // binary 11 / int 3
	// DefaultFolderName default name of favorite folder
	DefaultFolderName = "默认收藏夹"
	// AllFidFlag all folder id flag
	AllFidFlag = -1
	// CDFlag cool down flag
	CDFlag = -1
	// search error code
	SearchErrWordIllegal = -110 // 非法搜索词错误
	// clean state
	StateAllowToClean = 0
	StateCleaning     = 1
	StateCleanCD      = 2
)

type VideoFolder struct {
	MediaId    int64      `json:"media_id"`
	Fid        int64      `json:"fid"`
	Mid        int64      `json:"mid"`
	Name       string     `json:"name"`
	MaxCount   int        `json:"max_count"`
	CurCount   int        `json:"cur_count"`
	AttenCount int        `json:"atten_count"`
	Favoured   int8       `json:"favoured"`
	State      int8       `json:"state"`
	CTime      xtime.Time `json:"ctime"`
	MTime      xtime.Time `json:"mtime"`
	Cover      []*Cover   `json:"cover,omitempty"`
}

// IsPublic return true if folder is public.
func (f *VideoFolder) IsPublic() bool {
	return f.State&bit1 == int8(0)
}

// IsDefault return true if folder is default.
func (f *VideoFolder) IsDefault() bool {
	return f.State&bit2 == int8(0)
}

// StatePub return folder's public state.
func (f *VideoFolder) StatePub() int8 {
	return f.State & bit1
}

// StateDef return folder's default state.
func (f *VideoFolder) StateDef() int8 {
	return f.State & bit2
}

// IsDefault return true if state is default state.
func IsDefault(state int8) bool {
	return (state&(int8(1)<<1) == int8(0))
}

// CheckPublic check user update public value in [0, 1].
func CheckPublic(state int8) bool {
	return state == int8(0) || state == bit1
}

type VideoFolders []*VideoFolder

func (f VideoFolders) Len() int { return len(f) }

func (f VideoFolders) Less(i, j int) bool {
	if f[i].IsDefault() {
		return true
	}
	if f[j].IsDefault() {
		return false
	}
	if f[i].Fid > f[j].Fid {
		return true
	}
	return false
}

func (f VideoFolders) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

// Cover image
type Cover struct {
	Aid  int64  `json:"aid"`
	Pic  string `json:"pic"`
	Type int32  `json:"type"`
}

// VideoFolderSort folder index.
type VideoFolderSort struct {
	ID    int64              `json:"id"`
	Mid   int64              `json:"mid"`
	Sort  []int64            `json:"sort"`
	Map   map[int64]struct{} `json:"map"`
	CTime xtime.Time         `json:"ctime"`
	MTime xtime.Time         `json:"mtime"`
}

// Index return index for fids.
func (f *VideoFolderSort) Index() []byte {
	var (
		i  int
		v  int64
		fs = f.Sort
		n  = len(fs) * 8
		b  = make([]byte, n)
	)
	for i = 0; i < n; i += 8 {
		v = fs[i/8]
		b[i] = byte(v >> 56)
		b[i+1] = byte(v >> 48)
		b[i+2] = byte(v >> 40)
		b[i+3] = byte(v >> 32)
		b[i+4] = byte(v >> 24)
		b[i+5] = byte(v >> 16)
		b[i+6] = byte(v >> 8)
		b[i+7] = byte(v)
	}
	return b
}

// SetIndex set sort fids.
func (f *VideoFolderSort) SetIndex(b []byte) (err error) {
	var (
		i   int
		id  int64
		n   = len(b)
		ids = make([]int64, n)
	)
	if len(b)%8 != 0 {
		err = fmt.Errorf("invalid sort index:%v", b)
		return
	}
	f.Map = make(map[int64]struct{}, n)
	for i = 0; i < n; i += 8 {
		id = int64(b[i+7]) |
			int64(b[i+6])<<8 |
			int64(b[i+5])<<16 |
			int64(b[i+4])<<24 |
			int64(b[i+3])<<32 |
			int64(b[i+2])<<40 |
			int64(b[i+1])<<48 |
			int64(b[i])<<56
		ids[i/8] = id
		f.Map[id] = struct{}{}
	}
	f.Sort = ids
	return
}

// SortFavs sort the favorites by index.
func (f *VideoFolderSort) SortFavs(fs map[int64]*VideoFolder, isSelf bool) (res []*VideoFolder, update bool) {
	var (
		ok     bool
		id     int64
		sorted []int64
		fav    *VideoFolder
		idx    = f.Sort
	)
	res = make([]*VideoFolder, 0, len(fs))
	if len(f.Sort) == 0 {
		for _, fav = range fs {
			if !isSelf && !fav.IsPublic() {
				continue
			}
			res = append(res, fav)
		}
		sort.Sort(VideoFolders(res))
		return
	}
	if len(idx) != len(fs) {
		sorted = append(sorted, idx[0])
		for id = range fs {
			if _, ok = f.Map[id]; !ok {
				sorted = append(sorted, id)
			}
		}
		for _, id := range idx[1:] {
			if _, ok = fs[id]; ok {
				sorted = append(sorted, id)
			}
		}
		update = true
		f.Sort = sorted
	}
	for _, id = range f.Sort {
		if fav, ok = fs[id]; ok {
			if !isSelf && !fav.IsPublic() {
				continue
			}
			res = append(res, fav)
		}
	}
	return
}
